https_client.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #include "libnetdata/libnetdata.h"
  2. #include "https_client.h"
  3. #include "../mqtt_websockets/c-rbuf/include/ringbuffer.h"
  4. enum http_parse_state {
  5. HTTP_PARSE_INITIAL = 0,
  6. HTTP_PARSE_HEADERS,
  7. HTTP_PARSE_CONTENT
  8. };
  9. typedef struct {
  10. enum http_parse_state state;
  11. int content_length;
  12. int http_code;
  13. } http_parse_ctx;
  14. #define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
  15. #define NEED_MORE_DATA 0
  16. #define PARSE_SUCCESS 1
  17. #define PARSE_ERROR -1
  18. #define HTTP_LINE_TERM "\x0D\x0A"
  19. #define RESP_PROTO "HTTP/1.1 "
  20. #define HTTP_KEYVAL_SEPARATOR ": "
  21. #define HTTP_HDR_BUFFER_SIZE 256
  22. #define PORT_STR_MAX_BYTES 7
  23. static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
  24. {
  25. // currently we care only about content-length
  26. // but in future the way this is written
  27. // it can be extended
  28. if (!strcmp("content-length", key)) {
  29. parse_ctx->content_length = atoi(val);
  30. }
  31. }
  32. static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
  33. {
  34. int idx, idx_end;
  35. char buf_key[HTTP_HDR_BUFFER_SIZE];
  36. char buf_val[HTTP_HDR_BUFFER_SIZE];
  37. char *ptr = buf_key;
  38. if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
  39. error("CRLF expected");
  40. return 1;
  41. }
  42. char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
  43. if (!separator) {
  44. error("Missing Key/Value separator");
  45. return 1;
  46. }
  47. if (idx >= HTTP_HDR_BUFFER_SIZE) {
  48. error("Key name is too long");
  49. return 1;
  50. }
  51. rbuf_pop(buf, buf_key, idx);
  52. buf_key[idx] = 0;
  53. rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
  54. idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
  55. if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
  56. error("Value of key \"%s\" too long", buf_key);
  57. return 1;
  58. }
  59. rbuf_pop(buf, buf_val, idx_end);
  60. buf_val[idx_end] = 0;
  61. rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
  62. for (ptr = buf_key; *ptr; ptr++)
  63. *ptr = tolower(*ptr);
  64. process_http_hdr(parse_ctx, buf_key, buf_val);
  65. return 0;
  66. }
  67. static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
  68. {
  69. int idx;
  70. char rc[4];
  71. do {
  72. if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
  73. return NEED_MORE_DATA;
  74. switch (parse_ctx->state) {
  75. case HTTP_PARSE_INITIAL:
  76. if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
  77. error("Expected response to start with \"%s\"", RESP_PROTO);
  78. return PARSE_ERROR;
  79. }
  80. rbuf_bump_tail(buf, strlen(RESP_PROTO));
  81. if (rbuf_pop(buf, rc, 4) != 4) {
  82. error("Expected HTTP status code");
  83. return PARSE_ERROR;
  84. }
  85. if (rc[3] != ' ') {
  86. error("Expected space after HTTP return code");
  87. return PARSE_ERROR;
  88. }
  89. rc[3] = 0;
  90. parse_ctx->http_code = atoi(rc);
  91. if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
  92. error("HTTP code not in range 100 to 599");
  93. return PARSE_ERROR;
  94. }
  95. rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
  96. rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
  97. parse_ctx->state = HTTP_PARSE_HEADERS;
  98. break;
  99. case HTTP_PARSE_HEADERS:
  100. if (!idx) {
  101. parse_ctx->state = HTTP_PARSE_CONTENT;
  102. rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
  103. break;
  104. }
  105. if (parse_http_hdr(buf, parse_ctx))
  106. return PARSE_ERROR;
  107. rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
  108. rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
  109. break;
  110. case HTTP_PARSE_CONTENT:
  111. if (parse_ctx->content_length < 0) {
  112. error("content-length missing and http headers ended");
  113. return PARSE_ERROR;
  114. }
  115. if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
  116. return PARSE_SUCCESS;
  117. return NEED_MORE_DATA;
  118. }
  119. } while(1);
  120. }
  121. int https_request(http_req_type_t method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
  122. {
  123. struct timeval timeout = { .tv_sec = 30, .tv_usec = 0 };
  124. char sport[PORT_STR_MAX_BYTES];
  125. size_t len = 0;
  126. int rc = 1;
  127. int ret;
  128. char *ptr;
  129. http_parse_ctx parse_ctx = HTTP_PARSE_CTX_INITIALIZER;
  130. rbuf_t buffer = rbuf_create(b_size);
  131. if (!buffer)
  132. return 1;
  133. snprintf(sport, PORT_STR_MAX_BYTES, "%d", port);
  134. if (payload != NULL)
  135. len = strlen(payload);
  136. snprintf(
  137. b,
  138. b_size,
  139. "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: application/json\r\nContent-length: %zu\r\nAccept-Language: en-us\r\n"
  140. "User-Agent: Netdata/rocks\r\n\r\n",
  141. (method == HTTP_REQ_GET ? "GET" : "POST"), url, host, len);
  142. if (payload != NULL)
  143. strncat(b, payload, b_size - len);
  144. len = strlen(b);
  145. debug(D_ACLK, "Sending HTTPS req (%zu bytes): '%s'", len, b);
  146. int sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, host, 0, sport, &timeout);
  147. if (unlikely(sock == -1)) {
  148. error("Handshake failed");
  149. goto exit_buf;
  150. }
  151. SSL_CTX *ctx = security_initialize_openssl_client();
  152. if (ctx==NULL) {
  153. error("Cannot allocate SSL context");
  154. goto exit_sock;
  155. }
  156. // Certificate chain: not updating the stores - do we need private CA roots?
  157. // Calls to SSL_CTX_load_verify_locations would go here.
  158. SSL *ssl = SSL_new(ctx);
  159. if (ssl==NULL) {
  160. error("Cannot allocate SSL");
  161. goto exit_CTX;
  162. }
  163. SSL_set_fd(ssl, sock);
  164. ret = SSL_connect(ssl);
  165. if (ret != 1) {
  166. error("SSL_connect() failed with err=%d", ret);
  167. goto exit_SSL;
  168. }
  169. ret = SSL_write(ssl, b, len);
  170. if (ret <= 0)
  171. {
  172. error("SSL_write() failed with err=%d", ret);
  173. goto exit_SSL;
  174. }
  175. b[0] = 0;
  176. do {
  177. ptr = rbuf_get_linear_insert_range(buffer, &len);
  178. ret = SSL_read(ssl, ptr, len - 1);
  179. if (ret)
  180. rbuf_bump_head(buffer, ret);
  181. if (ret <= 0)
  182. {
  183. error("No response available - SSL_read()=%d", ret);
  184. goto exit_FULL;
  185. }
  186. } while (!(ret = parse_http_response(buffer, &parse_ctx)));
  187. if (ret != PARSE_SUCCESS) {
  188. error("Error parsing HTTP response");
  189. goto exit_FULL;
  190. }
  191. if (parse_ctx.http_code < 200 || parse_ctx.http_code >= 300) {
  192. error("HTTP Response not Success (got %d)", parse_ctx.http_code);
  193. goto exit_FULL;
  194. }
  195. len = rbuf_pop(buffer, b, b_size);
  196. b[MIN(len, b_size-1)] = 0;
  197. rc = 0;
  198. exit_FULL:
  199. exit_SSL:
  200. SSL_free(ssl);
  201. exit_CTX:
  202. SSL_CTX_free(ctx);
  203. exit_sock:
  204. close(sock);
  205. exit_buf:
  206. rbuf_free(buffer);
  207. return rc;
  208. }