Proxy.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*******************************************************************************
  2. * Copyright (c) 2009, 2025 Diehl Metering, Ian Craggs and others
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v2.0
  6. * and Eclipse Distribution License v1.0 which accompany this distribution.
  7. *
  8. * The Eclipse Public License is available at
  9. * https://www.eclipse.org/legal/epl-2.0/
  10. * and the Eclipse Distribution License is available at
  11. * http://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * Contributors:
  14. * Keith Holman - initial implementation and documentation
  15. * Sven Gambel - move WebSocket proxy support to generic proxy support
  16. *******************************************************************************/
  17. #include <stdio.h>
  18. #include <string.h>
  19. // for timeout process in Proxy_connect()
  20. #include <time.h>
  21. #if defined(_WIN32) || defined(_WIN64)
  22. #include <windows.h>
  23. /* Windows doesn't have strtok_r, so remap it to strtok_s */
  24. #define strtok_r strtok_s
  25. #if defined(_MSC_VER) && _MSC_VER < 1900
  26. #define snprintf _snprintf
  27. #endif
  28. #else
  29. #include <unistd.h>
  30. #endif
  31. #include "Log.h"
  32. #include "MQTTProtocolOut.h"
  33. #include "StackTrace.h"
  34. #include "Heap.h"
  35. #if defined(OPENSSL)
  36. #include "SSLSocket.h"
  37. #include <openssl/rand.h>
  38. #endif /* defined(OPENSSL) */
  39. #include "Socket.h"
  40. #include "Base64.h"
  41. #include "ctype.h"
  42. /**
  43. * Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint.
  44. *
  45. * @param[in] net network connection to proxy.
  46. * @param[in] ssl enable ssl.
  47. * @param[in] hostname hostname of endpoint.
  48. *
  49. * @retval SOCKET_ERROR failed to network connection
  50. * @retval 0 connection to endpoint
  51. *
  52. */
  53. int Proxy_connect(networkHandles *net, int ssl, const char *hostname)
  54. {
  55. int port, i, rc = 0, buf_len=0;
  56. char *buf = NULL;
  57. size_t hostname_len, actual_len = 0;
  58. time_t current, timeout;
  59. PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
  60. FUNC_ENTRY;
  61. hostname_len = MQTTProtocol_addressPort(hostname, &port, NULL, PROXY_DEFAULT_PORT);
  62. for ( i = 0; i < 2; ++i ) {
  63. #if defined(OPENSSL)
  64. if(ssl) {
  65. if (net->https_proxy_auth) {
  66. buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
  67. "Host: %.*s\r\n"
  68. "Proxy-authorization: Basic %s\r\n"
  69. "\r\n",
  70. (int)hostname_len, hostname, port,
  71. (int)hostname_len, hostname, net->https_proxy_auth);
  72. }
  73. else {
  74. buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
  75. "Host: %.*s\r\n"
  76. "\r\n",
  77. (int)hostname_len, hostname, port,
  78. (int)hostname_len, hostname);
  79. }
  80. }
  81. else {
  82. #endif
  83. if (net->http_proxy_auth) {
  84. buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
  85. "Host: %.*s\r\n"
  86. "Proxy-authorization: Basic %s\r\n"
  87. "\r\n",
  88. (int)hostname_len, hostname, port,
  89. (int)hostname_len, hostname, net->http_proxy_auth);
  90. }
  91. else {
  92. buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
  93. "Host: %.*s\r\n"
  94. "\r\n",
  95. (int)hostname_len, hostname, port,
  96. (int)hostname_len, hostname);
  97. }
  98. #if defined(OPENSSL)
  99. }
  100. #endif
  101. if ( i==0 && buf_len > 0 ) {
  102. ++buf_len;
  103. if ((buf = malloc( buf_len )) == NULL)
  104. {
  105. rc = PAHO_MEMORY_ERROR;
  106. goto exit;
  107. }
  108. }
  109. }
  110. Log(TRACE_PROTOCOL, -1, "Proxy_connect: \"%s\"", buf);
  111. Socket_putdatas(net->socket, buf, buf_len, nulbufs);
  112. free(buf);
  113. buf = NULL;
  114. time(&timeout);
  115. timeout += (time_t)10;
  116. while(1) {
  117. buf = Socket_getdata(net->socket, (size_t)12, &actual_len, &rc);
  118. if(actual_len) {
  119. if ( (strncmp( buf, "HTTP/1.0 200", 12 ) != 0) && (strncmp( buf, "HTTP/1.1 200", 12 ) != 0) )
  120. rc = SOCKET_ERROR;
  121. break;
  122. }
  123. else {
  124. time(&current);
  125. if(current > timeout) {
  126. rc = SOCKET_ERROR;
  127. break;
  128. }
  129. #if defined(_WIN32) || defined(_WIN64)
  130. Sleep(250);
  131. #else
  132. usleep(250000);
  133. #endif
  134. }
  135. }
  136. /* flush the SocketBuffer */
  137. actual_len = 1;
  138. while (actual_len)
  139. {
  140. int rc1;
  141. buf = Socket_getdata(net->socket, (size_t)1, &actual_len, &rc1);
  142. }
  143. exit:
  144. FUNC_EXIT_RC(rc);
  145. return rc;
  146. }
  147. /**
  148. * Check the dest parameter against the no_proxy blacklist
  149. *
  150. * no_proxy:
  151. * 1. Use lowercase form.
  152. * 2. Use comma-separated hostname:port values.
  153. * 3. IP addresses are okay, but hostnames are never resolved.
  154. * 4. Suffixes are always matched (e.g. example.com will match test.example.com).
  155. * 5. If top-level domains need to be matched, leading dots are accepted e.g. .com
  156. *
  157. * @param dest the destination hostname/ip address
  158. * @param no_proxy the no_proxy list, probably from the environment
  159. * @return 1 - use the proxy, 0 - don't use the proxy
  160. */
  161. int Proxy_noProxy(const char* dest, char* no_proxy)
  162. {
  163. char* saveptr = NULL;
  164. char* curtok = NULL;
  165. int port = 0;
  166. int destport = 0;
  167. int port_matches = 0;
  168. size_t hostlen = 0;
  169. size_t desthostlen = 0;
  170. int rc = 1;
  171. char* no_proxy_list = NULL;
  172. if ((no_proxy_list = MQTTStrdup(no_proxy)) == NULL)
  173. {
  174. rc = PAHO_MEMORY_ERROR;
  175. goto exit;
  176. }
  177. curtok = strtok_r(no_proxy_list, ",", &saveptr);
  178. while (curtok)
  179. {
  180. char* host = curtok;
  181. int matched = 0;
  182. int pos = 1;
  183. const char* topic;
  184. if (curtok == NULL)
  185. break;
  186. if (host[0] == '.')
  187. host++;
  188. hostlen = MQTTProtocol_addressPort(host, &port, &topic, -99);
  189. desthostlen = MQTTProtocol_addressPort(dest, &destport, &topic, -99);
  190. if (dest[desthostlen] == '/')
  191. desthostlen--;
  192. /* check if port matches */
  193. if (port == -99)
  194. port_matches = 1; /* no_proxy port absence matches any */
  195. else if (destport != -99)
  196. {
  197. if (destport == port)
  198. port_matches = 1;
  199. }
  200. if (memcmp(host, "*", 1) == 0) /* * matches anything */
  201. {
  202. /* match any host or address */
  203. if (port_matches == 1)
  204. {
  205. rc = 0;
  206. break;
  207. }
  208. }
  209. /* now see if .host matches the end of the dest string */
  210. /* match backwards from the end */
  211. while (host[hostlen - pos] == dest[desthostlen - pos])
  212. {
  213. if (pos == hostlen) /* reached the beginning of the no_proxy definition */
  214. {
  215. if ((pos == desthostlen || dest[desthostlen - pos - 1] == '.') && port_matches)
  216. matched = 1;
  217. break;
  218. }
  219. else if (pos == desthostlen) /* reached the beginning of the destination string */
  220. break;
  221. pos++;
  222. }
  223. if (matched)
  224. {
  225. rc = 0;
  226. break;
  227. }
  228. curtok = strtok_r(NULL, ",", &saveptr);
  229. }
  230. if (rc == 0)
  231. Log(TRACE_PROTOCOL, -1, "Matched destination %s against no_proxy %s. Don't use proxy.", dest, curtok);
  232. free(no_proxy_list);
  233. exit:
  234. return rc;
  235. }
  236. /**
  237. * Allow user or password characters to be expressed in the form of %XX, XX being the
  238. * hexadecimal value of the character. This will avoid problems when a user code or a password
  239. * contains a '@' or another special character ('%' included)
  240. * @param p0 output string
  241. * @param p1 input string
  242. * @param basic_auth_in_len
  243. */
  244. void Proxy_specialChars(char* p0, char* p1, b64_size_t *basic_auth_in_len)
  245. {
  246. while (*p1 != '@')
  247. {
  248. if (*p1 != '%')
  249. {
  250. *p0++ = *p1++;
  251. }
  252. else if (isxdigit(*(p1 + 1)) && isxdigit(*(p1 + 2)))
  253. {
  254. /* next 2 characters are hexa digits */
  255. char hex[3];
  256. p1++;
  257. hex[0] = *p1++;
  258. hex[1] = *p1++;
  259. hex[2] = '\0';
  260. *p0++ = (char)strtol(hex, 0, 16);
  261. /* 3 input char => 1 output char */
  262. *basic_auth_in_len -= 2;
  263. }
  264. }
  265. *p0 = 0x0;
  266. }
  267. /**
  268. * Set the HTTP proxy for connecting
  269. * Examples of proxy settings:
  270. * http://your.proxy.server:8080/
  271. * http://user:pass@my.proxy.server:8080/
  272. *
  273. * @param aClient pointer to Clients object
  274. * @param source the proxy setting from environment or API
  275. * @param [out] dest pointer to output proxy info
  276. * @param [out] auth_dest pointer to output authentication material
  277. * @param prefix expected URI prefix: http:// or https://
  278. * @return 0 on success, non-zero otherwise
  279. */
  280. int Proxy_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix)
  281. {
  282. b64_size_t basic_auth_in_len, basic_auth_out_len;
  283. b64_data_t *basic_auth;
  284. char *p1;
  285. int rc = 0;
  286. if (*auth_dest)
  287. {
  288. free(*auth_dest);
  289. *auth_dest = NULL;
  290. }
  291. if (source)
  292. {
  293. if ((p1 = strstr(source, prefix)) != NULL) /* skip http:// prefix, if any */
  294. source += strlen(prefix);
  295. *dest = source;
  296. if ((p1 = strchr(source, '@')) != NULL) /* find user.pass separator */
  297. *dest = p1 + 1;
  298. if (p1)
  299. {
  300. /* basic auth len is string between http:// and @ */
  301. basic_auth_in_len = (b64_size_t)(p1 - source);
  302. if (basic_auth_in_len > 0)
  303. {
  304. basic_auth = (b64_data_t *)malloc(sizeof(char)*(basic_auth_in_len+1));
  305. if (!basic_auth)
  306. {
  307. rc = PAHO_MEMORY_ERROR;
  308. goto exit;
  309. }
  310. Proxy_specialChars((char*)basic_auth, source, &basic_auth_in_len);
  311. basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len) + 1; /* add 1 for trailing NULL */
  312. if ((*auth_dest = (char *)malloc(sizeof(char)*basic_auth_out_len)) == NULL)
  313. {
  314. free(basic_auth);
  315. rc = PAHO_MEMORY_ERROR;
  316. goto exit;
  317. }
  318. Base64_encode(*auth_dest, basic_auth_out_len, basic_auth, basic_auth_in_len);
  319. free(basic_auth);
  320. }
  321. }
  322. }
  323. exit:
  324. return rc;
  325. }