aclk_lws_https_client.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define ACLK_LWS_HTTPS_CLIENT_INTERNAL
  3. #include "aclk_lws_https_client.h"
  4. #include "aclk_common.h"
  5. #include "aclk_lws_wss_client.h"
  6. #define SMALL_BUFFER 16
  7. struct simple_hcc_data {
  8. char *data;
  9. size_t data_size;
  10. size_t written;
  11. char lws_work_buffer[1024 + LWS_PRE];
  12. char *payload;
  13. int response_code;
  14. int done;
  15. };
  16. static int simple_https_client_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
  17. {
  18. UNUSED(user);
  19. int n;
  20. char *ptr;
  21. char buffer[SMALL_BUFFER];
  22. struct simple_hcc_data *perconn_data = lws_get_opaque_user_data(wsi);
  23. switch (reason) {
  24. case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
  25. debug(D_ACLK, "LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ");
  26. if (perconn_data->data_size - 1 - perconn_data->written < len)
  27. return 1;
  28. memcpy(&perconn_data->data[perconn_data->written], in, len);
  29. perconn_data->written += len;
  30. return 0;
  31. case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
  32. debug(D_ACLK, "LWS_CALLBACK_RECEIVE_CLIENT_HTTP");
  33. if(!perconn_data) {
  34. error("Missing Per Connect Data");
  35. return -1;
  36. }
  37. n = sizeof(perconn_data->lws_work_buffer) - LWS_PRE;
  38. ptr = perconn_data->lws_work_buffer + LWS_PRE;
  39. if (lws_http_client_read(wsi, &ptr, &n) < 0)
  40. return -1;
  41. perconn_data->data[perconn_data->written] = '\0';
  42. return 0;
  43. case LWS_CALLBACK_WSI_DESTROY:
  44. debug(D_ACLK, "LWS_CALLBACK_WSI_DESTROY");
  45. if(perconn_data)
  46. perconn_data->done = 1;
  47. return 0;
  48. case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
  49. debug(D_ACLK, "LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP");
  50. if(perconn_data)
  51. perconn_data->response_code = lws_http_client_http_response(wsi);
  52. return 0;
  53. case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
  54. debug(D_ACLK, "LWS_CALLBACK_CLOSED_CLIENT_HTTP");
  55. return 0;
  56. case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
  57. debug(D_ACLK, "LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS");
  58. return 0;
  59. case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
  60. debug(D_ACLK, "LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER");
  61. if(perconn_data && perconn_data->payload) {
  62. unsigned char **p = (unsigned char **)in, *end = (*p) + len;
  63. snprintfz(buffer, SMALL_BUFFER, "%zu", strlen(perconn_data->payload));
  64. if (lws_add_http_header_by_token(wsi,
  65. WSI_TOKEN_HTTP_CONTENT_LENGTH,
  66. (unsigned char *)buffer, strlen(buffer), p, end))
  67. return -1;
  68. if (lws_add_http_header_by_token(wsi,
  69. WSI_TOKEN_HTTP_CONTENT_TYPE,
  70. (unsigned char *)ACLK_CONTENT_TYPE_JSON,
  71. strlen(ACLK_CONTENT_TYPE_JSON), p, end))
  72. return -1;
  73. lws_client_http_body_pending(wsi, 1);
  74. lws_callback_on_writable(wsi);
  75. }
  76. return 0;
  77. case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
  78. debug(D_ACLK, "LWS_CALLBACK_CLIENT_HTTP_WRITEABLE");
  79. if(perconn_data && perconn_data->payload) {
  80. n = strlen(perconn_data->payload);
  81. if(perconn_data->data_size < (size_t)LWS_PRE + n + 1) {
  82. error("Buffer given is not big enough");
  83. return 1;
  84. }
  85. memcpy(&perconn_data->data[LWS_PRE], perconn_data->payload, n);
  86. if(n != lws_write(wsi, (unsigned char*)&perconn_data->data[LWS_PRE], n, LWS_WRITE_HTTP)) {
  87. error("lws_write error");
  88. perconn_data->data[0] = 0;
  89. return 1;
  90. }
  91. lws_client_http_body_pending(wsi, 0);
  92. // clean for subsequent reply read
  93. perconn_data->data[0] = 0;
  94. }
  95. return 0;
  96. case LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL:
  97. debug(D_ACLK, "LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL");
  98. return 0;
  99. case LWS_CALLBACK_WSI_CREATE:
  100. debug(D_ACLK, "LWS_CALLBACK_WSI_CREATE");
  101. return 0;
  102. case LWS_CALLBACK_PROTOCOL_INIT:
  103. debug(D_ACLK, "LWS_CALLBACK_PROTOCOL_INIT");
  104. return 0;
  105. case LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL:
  106. debug(D_ACLK, "LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL");
  107. return 0;
  108. case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
  109. debug(D_ACLK, "LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED");
  110. return 0;
  111. case LWS_CALLBACK_GET_THREAD_ID:
  112. debug(D_ACLK, "LWS_CALLBACK_GET_THREAD_ID");
  113. return 0;
  114. case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
  115. debug(D_ACLK, "LWS_CALLBACK_EVENT_WAIT_CANCELLED");
  116. return 0;
  117. case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION:
  118. debug(D_ACLK, "LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION");
  119. return 0;
  120. case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
  121. debug(D_ACLK, "LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH");
  122. return 0;
  123. default:
  124. debug(D_ACLK, "Unknown callback %d", (int)reason);
  125. return 0;
  126. }
  127. }
  128. static const struct lws_protocols protocols[] = {
  129. {
  130. "http",
  131. simple_https_client_callback,
  132. 0,
  133. 0,
  134. 0,
  135. 0,
  136. 0
  137. },
  138. { NULL, NULL, 0, 0, 0, 0, 0 }
  139. };
  140. static void simple_hcc_log_divert(int level, const char *line)
  141. {
  142. UNUSED(level);
  143. error("Libwebsockets: %s", line);
  144. }
  145. int aclk_send_https_request(char *method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
  146. {
  147. info("%s %s", __func__, method);
  148. struct lws_context_creation_info info;
  149. struct lws_client_connect_info i;
  150. struct lws_context *context;
  151. struct simple_hcc_data *data = callocz(1, sizeof(struct simple_hcc_data));
  152. data->data = b;
  153. data->data[0] = 0;
  154. data->data_size = b_size;
  155. data->payload = payload;
  156. int n = 0;
  157. time_t timestamp;
  158. struct lws_vhost *vhost;
  159. memset(&info, 0, sizeof info);
  160. info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
  161. info.port = CONTEXT_PORT_NO_LISTEN;
  162. info.protocols = protocols;
  163. context = lws_create_context(&info);
  164. if (!context) {
  165. error("Error creating LWS context");
  166. freez(data);
  167. return 1;
  168. }
  169. lws_set_log_level(LLL_ERR | LLL_WARN, simple_hcc_log_divert);
  170. lws_service(context, 0);
  171. memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
  172. i.context = context;
  173. #ifdef ACLK_SSL_ALLOW_SELF_SIGNED
  174. i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_INSECURE;
  175. info("Disabling SSL certificate checks");
  176. #else
  177. i.ssl_connection = LCCSCF_USE_SSL;
  178. #endif
  179. #if defined(HAVE_X509_VERIFY_PARAM_set1_host) && HAVE_X509_VERIFY_PARAM_set1_host == 0
  180. #warning DISABLING SSL HOSTNAME VALIDATION BECAUSE IT IS NOT AVAILABLE ON THIS SYSTEM.
  181. i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
  182. #endif
  183. i.port = port;
  184. i.address = host;
  185. i.path = url;
  186. i.host = i.address;
  187. i.origin = i.address;
  188. i.method = method;
  189. i.opaque_user_data = data;
  190. i.alpn = "http/1.1";
  191. i.protocol = protocols[0].name;
  192. vhost = lws_get_vhost_by_name(context, "default");
  193. if(!vhost)
  194. fatal("Could not find the default LWS vhost.");
  195. //set up proxy
  196. aclk_wss_set_proxy(vhost);
  197. lws_client_connect_via_info(&i);
  198. // libwebsockets handle connection timeouts already
  199. // this adds additional safety in case of bug in LWS
  200. timestamp = now_monotonic_sec();
  201. while( n >= 0 && !data->done && !netdata_exit) {
  202. n = lws_service(context, 0);
  203. if( now_monotonic_sec() - timestamp > SEND_HTTPS_REQUEST_TIMEOUT ) {
  204. data->data[0] = 0;
  205. data->done = 1;
  206. error("Servicing LWS took too long.");
  207. }
  208. }
  209. lws_context_destroy(context);
  210. n = data->response_code;
  211. freez(data);
  212. return (n < 200 || n >= 300);
  213. }