https_client.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "libnetdata/libnetdata.h"
  3. #include "https_client.h"
  4. #include "mqtt_websockets/c-rbuf/include/ringbuffer.h"
  5. #include "aclk_util.h"
  6. #include "daemon/global_statistics.h"
  7. enum http_parse_state {
  8. HTTP_PARSE_INITIAL = 0,
  9. HTTP_PARSE_HEADERS,
  10. HTTP_PARSE_CONTENT
  11. };
  12. static const char *http_req_type_to_str(http_req_type_t req) {
  13. switch (req) {
  14. case HTTP_REQ_GET:
  15. return "GET";
  16. case HTTP_REQ_POST:
  17. return "POST";
  18. case HTTP_REQ_CONNECT:
  19. return "CONNECT";
  20. default:
  21. return "unknown";
  22. }
  23. }
  24. typedef struct {
  25. enum http_parse_state state;
  26. int content_length;
  27. int http_code;
  28. } http_parse_ctx;
  29. #define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
  30. static inline void http_parse_ctx_clear(http_parse_ctx *ctx) {
  31. ctx->state = HTTP_PARSE_INITIAL;
  32. ctx->content_length = -1;
  33. ctx->http_code = 0;
  34. }
  35. #define POLL_TO_MS 100
  36. #define NEED_MORE_DATA 0
  37. #define PARSE_SUCCESS 1
  38. #define PARSE_ERROR -1
  39. #define HTTP_LINE_TERM "\x0D\x0A"
  40. #define RESP_PROTO "HTTP/1.1 "
  41. #define HTTP_KEYVAL_SEPARATOR ": "
  42. #define HTTP_HDR_BUFFER_SIZE 256
  43. #define PORT_STR_MAX_BYTES 12
  44. static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
  45. {
  46. // currently we care only about content-length
  47. // but in future the way this is written
  48. // it can be extended
  49. if (!strcmp("content-length", key)) {
  50. parse_ctx->content_length = atoi(val);
  51. }
  52. }
  53. static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
  54. {
  55. int idx, idx_end;
  56. char buf_key[HTTP_HDR_BUFFER_SIZE];
  57. char buf_val[HTTP_HDR_BUFFER_SIZE];
  58. char *ptr = buf_key;
  59. if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
  60. error("CRLF expected");
  61. return 1;
  62. }
  63. char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
  64. if (!separator) {
  65. error("Missing Key/Value separator");
  66. return 1;
  67. }
  68. if (idx >= HTTP_HDR_BUFFER_SIZE) {
  69. error("Key name is too long");
  70. return 1;
  71. }
  72. rbuf_pop(buf, buf_key, idx);
  73. buf_key[idx] = 0;
  74. rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
  75. idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
  76. if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
  77. error("Value of key \"%s\" too long", buf_key);
  78. return 1;
  79. }
  80. rbuf_pop(buf, buf_val, idx_end);
  81. buf_val[idx_end] = 0;
  82. for (ptr = buf_key; *ptr; ptr++)
  83. *ptr = tolower(*ptr);
  84. process_http_hdr(parse_ctx, buf_key, buf_val);
  85. return 0;
  86. }
  87. static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
  88. {
  89. int idx;
  90. char rc[4];
  91. do {
  92. if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
  93. return NEED_MORE_DATA;
  94. switch (parse_ctx->state) {
  95. case HTTP_PARSE_INITIAL:
  96. if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
  97. error("Expected response to start with \"%s\"", RESP_PROTO);
  98. return PARSE_ERROR;
  99. }
  100. rbuf_bump_tail(buf, strlen(RESP_PROTO));
  101. if (rbuf_pop(buf, rc, 4) != 4) {
  102. error("Expected HTTP status code");
  103. return PARSE_ERROR;
  104. }
  105. if (rc[3] != ' ') {
  106. error("Expected space after HTTP return code");
  107. return PARSE_ERROR;
  108. }
  109. rc[3] = 0;
  110. parse_ctx->http_code = atoi(rc);
  111. if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
  112. error("HTTP code not in range 100 to 599");
  113. return PARSE_ERROR;
  114. }
  115. rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
  116. rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
  117. parse_ctx->state = HTTP_PARSE_HEADERS;
  118. break;
  119. case HTTP_PARSE_HEADERS:
  120. if (!idx) {
  121. parse_ctx->state = HTTP_PARSE_CONTENT;
  122. rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
  123. break;
  124. }
  125. if (parse_http_hdr(buf, parse_ctx))
  126. return PARSE_ERROR;
  127. rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
  128. rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
  129. break;
  130. case HTTP_PARSE_CONTENT:
  131. // replies like CONNECT etc. do not have content
  132. if (parse_ctx->content_length < 0)
  133. return PARSE_SUCCESS;
  134. if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
  135. return PARSE_SUCCESS;
  136. return NEED_MORE_DATA;
  137. }
  138. } while(1);
  139. }
  140. typedef struct https_req_ctx {
  141. https_req_t *request;
  142. int sock;
  143. rbuf_t buf_rx;
  144. struct pollfd poll_fd;
  145. SSL_CTX *ssl_ctx;
  146. SSL *ssl;
  147. size_t written;
  148. int self_signed_allowed;
  149. http_parse_ctx parse_ctx;
  150. time_t req_start_time;
  151. } https_req_ctx_t;
  152. static int https_req_check_timedout(https_req_ctx_t *ctx) {
  153. if (now_realtime_sec() > ctx->req_start_time + ctx->request->timeout_s) {
  154. error("request timed out");
  155. return 1;
  156. }
  157. return 0;
  158. }
  159. static char *_ssl_err_tos(int err)
  160. {
  161. switch(err){
  162. case SSL_ERROR_SSL:
  163. return "SSL_ERROR_SSL";
  164. case SSL_ERROR_WANT_READ:
  165. return "SSL_ERROR_WANT_READ";
  166. case SSL_ERROR_WANT_WRITE:
  167. return "SSL_ERROR_WANT_WRITE";
  168. case SSL_ERROR_NONE:
  169. return "SSL_ERROR_NONE";
  170. case SSL_ERROR_ZERO_RETURN:
  171. return "SSL_ERROR_ZERO_RETURN";
  172. case SSL_ERROR_WANT_CONNECT:
  173. return "SSL_ERROR_WANT_CONNECT";
  174. case SSL_ERROR_WANT_ACCEPT:
  175. return "SSL_ERROR_WANT_ACCEPT";
  176. }
  177. return "Unknown!!!";
  178. }
  179. static int socket_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
  180. ctx->written = 0;
  181. ctx->poll_fd.events = POLLOUT;
  182. do {
  183. int ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
  184. if (ret < 0) {
  185. error("poll error");
  186. return 1;
  187. }
  188. if (ret == 0) {
  189. if (https_req_check_timedout(ctx)) {
  190. error("Poll timed out");
  191. return 2;
  192. }
  193. continue;
  194. }
  195. ret = write(ctx->sock, &data[ctx->written], data_len - ctx->written);
  196. if (ret > 0) {
  197. ctx->written += ret;
  198. } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
  199. error("Error writing to socket");
  200. return 3;
  201. }
  202. } while (ctx->written < data_len);
  203. return 0;
  204. }
  205. static int ssl_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
  206. ctx->written = 0;
  207. ctx->poll_fd.events |= POLLOUT;
  208. do {
  209. int ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
  210. if (ret < 0) {
  211. error("poll error");
  212. return 1;
  213. }
  214. if (ret == 0) {
  215. if (https_req_check_timedout(ctx)) {
  216. error("Poll timed out");
  217. return 2;
  218. }
  219. continue;
  220. }
  221. ctx->poll_fd.events = 0;
  222. ret = SSL_write(ctx->ssl, &data[ctx->written], data_len - ctx->written);
  223. if (ret > 0) {
  224. ctx->written += ret;
  225. } else {
  226. ret = SSL_get_error(ctx->ssl, ret);
  227. switch (ret) {
  228. case SSL_ERROR_WANT_READ:
  229. ctx->poll_fd.events |= POLLIN;
  230. break;
  231. case SSL_ERROR_WANT_WRITE:
  232. ctx->poll_fd.events |= POLLOUT;
  233. break;
  234. default:
  235. error("SSL_write Err: %s", _ssl_err_tos(ret));
  236. return 3;
  237. }
  238. }
  239. } while (ctx->written < data_len);
  240. return 0;
  241. }
  242. static inline int https_client_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
  243. if (ctx->ssl_ctx)
  244. return ssl_write_all(ctx, data, data_len);
  245. return socket_write_all(ctx, data, data_len);
  246. }
  247. static int read_parse_response(https_req_ctx_t *ctx) {
  248. int ret;
  249. char *ptr;
  250. size_t size;
  251. ctx->poll_fd.events = POLLIN;
  252. do {
  253. ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
  254. if (ret < 0) {
  255. error("poll error");
  256. return 1;
  257. }
  258. if (ret == 0) {
  259. if (https_req_check_timedout(ctx)) {
  260. error("Poll timed out");
  261. return 2;
  262. }
  263. if (!ctx->ssl_ctx)
  264. continue;
  265. }
  266. ctx->poll_fd.events = 0;
  267. ptr = rbuf_get_linear_insert_range(ctx->buf_rx, &size);
  268. if (ctx->ssl_ctx)
  269. ret = SSL_read(ctx->ssl, ptr, size);
  270. else
  271. ret = read(ctx->sock, ptr, size);
  272. if (ret > 0) {
  273. rbuf_bump_head(ctx->buf_rx, ret);
  274. } else {
  275. if (ctx->ssl_ctx) {
  276. ret = SSL_get_error(ctx->ssl, ret);
  277. switch (ret) {
  278. case SSL_ERROR_WANT_READ:
  279. ctx->poll_fd.events |= POLLIN;
  280. break;
  281. case SSL_ERROR_WANT_WRITE:
  282. ctx->poll_fd.events |= POLLOUT;
  283. break;
  284. default:
  285. error("SSL_read Err: %s", _ssl_err_tos(ret));
  286. return 3;
  287. }
  288. } else {
  289. if (errno != EAGAIN && errno != EWOULDBLOCK) {
  290. error("write error");
  291. return 3;
  292. }
  293. ctx->poll_fd.events |= POLLIN;
  294. }
  295. }
  296. } while (!(ret = parse_http_response(ctx->buf_rx, &ctx->parse_ctx)));
  297. if (ret != PARSE_SUCCESS) {
  298. error("Error parsing HTTP response");
  299. return 1;
  300. }
  301. return 0;
  302. }
  303. #define TX_BUFFER_SIZE 8192
  304. #define RX_BUFFER_SIZE (TX_BUFFER_SIZE*2)
  305. static int handle_http_request(https_req_ctx_t *ctx) {
  306. BUFFER *hdr = buffer_create(TX_BUFFER_SIZE, &netdata_buffers_statistics.buffers_aclk);
  307. int rc = 0;
  308. http_parse_ctx_clear(&ctx->parse_ctx);
  309. // Prepare data to send
  310. switch (ctx->request->request_type) {
  311. case HTTP_REQ_CONNECT:
  312. buffer_strcat(hdr, "CONNECT ");
  313. break;
  314. case HTTP_REQ_GET:
  315. buffer_strcat(hdr, "GET ");
  316. break;
  317. case HTTP_REQ_POST:
  318. buffer_strcat(hdr, "POST ");
  319. break;
  320. default:
  321. error("Unknown HTTPS request type!");
  322. rc = 1;
  323. goto err_exit;
  324. }
  325. if (ctx->request->request_type == HTTP_REQ_CONNECT) {
  326. buffer_strcat(hdr, ctx->request->host);
  327. buffer_sprintf(hdr, ":%d", ctx->request->port);
  328. } else {
  329. buffer_strcat(hdr, ctx->request->url);
  330. }
  331. buffer_strcat(hdr, " HTTP/1.1\x0D\x0A");
  332. //TODO Headers!
  333. if (ctx->request->request_type != HTTP_REQ_CONNECT) {
  334. buffer_sprintf(hdr, "Host: %s\x0D\x0A", ctx->request->host);
  335. }
  336. buffer_strcat(hdr, "User-Agent: Netdata/rocks newhttpclient\x0D\x0A");
  337. if (ctx->request->request_type == HTTP_REQ_POST && ctx->request->payload && ctx->request->payload_size) {
  338. buffer_sprintf(hdr, "Content-Length: %zu\x0D\x0A", ctx->request->payload_size);
  339. }
  340. if (ctx->request->proxy_username) {
  341. size_t creds_plain_len = strlen(ctx->request->proxy_username) + strlen(ctx->request->proxy_password) + 1 /* ':' */;
  342. char *creds_plain = callocz(1, creds_plain_len + 1);
  343. char *ptr = creds_plain;
  344. strcpy(ptr, ctx->request->proxy_username);
  345. ptr += strlen(ctx->request->proxy_username);
  346. *ptr++ = ':';
  347. strcpy(ptr, ctx->request->proxy_password);
  348. int creds_base64_len = (((4 * creds_plain_len / 3) + 3) & ~3);
  349. // OpenSSL encoder puts newline every 64 output bytes
  350. // we remove those but during encoding we need that space in the buffer
  351. creds_base64_len += (1+(creds_base64_len/64)) * strlen("\n");
  352. char *creds_base64 = callocz(1, creds_base64_len + 1);
  353. base64_encode_helper((unsigned char*)creds_base64, &creds_base64_len, (unsigned char*)creds_plain, creds_plain_len);
  354. buffer_sprintf(hdr, "Proxy-Authorization: Basic %s\x0D\x0A", creds_base64);
  355. freez(creds_plain);
  356. }
  357. buffer_strcat(hdr, "\x0D\x0A");
  358. // Send the request
  359. if (https_client_write_all(ctx, hdr->buffer, hdr->len)) {
  360. error("Couldn't write HTTP request header into SSL connection");
  361. rc = 2;
  362. goto err_exit;
  363. }
  364. if (ctx->request->request_type == HTTP_REQ_POST && ctx->request->payload && ctx->request->payload_size) {
  365. if (https_client_write_all(ctx, ctx->request->payload, ctx->request->payload_size)) {
  366. error("Couldn't write payload into SSL connection");
  367. rc = 3;
  368. goto err_exit;
  369. }
  370. }
  371. // Read The Response
  372. if (read_parse_response(ctx)) {
  373. error("Error reading or parsing response from server");
  374. rc = 4;
  375. goto err_exit;
  376. }
  377. err_exit:
  378. buffer_free(hdr);
  379. return rc;
  380. }
  381. static int cert_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
  382. {
  383. X509 *err_cert;
  384. int err, depth;
  385. char *err_str;
  386. if (!preverify_ok) {
  387. err = X509_STORE_CTX_get_error(ctx);
  388. depth = X509_STORE_CTX_get_error_depth(ctx);
  389. err_cert = X509_STORE_CTX_get_current_cert(ctx);
  390. err_str = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0);
  391. error("Cert Chain verify error:num=%d:%s:depth=%d:%s", err,
  392. X509_verify_cert_error_string(err), depth, err_str);
  393. free(err_str);
  394. }
  395. #ifdef ACLK_SSL_ALLOW_SELF_SIGNED
  396. if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
  397. {
  398. preverify_ok = 1;
  399. error("Self Signed Certificate Accepted as the agent was built with ACLK_SSL_ALLOW_SELF_SIGNED");
  400. }
  401. #endif
  402. return preverify_ok;
  403. }
  404. int https_request(https_req_t *request, https_req_response_t *response) {
  405. int rc = 1, ret;
  406. char connect_port_str[PORT_STR_MAX_BYTES];
  407. const char *connect_host = request->proxy_host ? request->proxy_host : request->host;
  408. int connect_port = request->proxy_host ? request->proxy_port : request->port;
  409. struct timeval timeout = { .tv_sec = request->timeout_s, .tv_usec = 0 };
  410. https_req_ctx_t *ctx = callocz(1, sizeof(https_req_ctx_t));
  411. ctx->req_start_time = now_realtime_sec();
  412. ctx->buf_rx = rbuf_create(RX_BUFFER_SIZE);
  413. if (!ctx->buf_rx) {
  414. error("Couldn't allocate buffer for RX data");
  415. goto exit_req_ctx;
  416. }
  417. snprintfz(connect_port_str, PORT_STR_MAX_BYTES, "%d", connect_port);
  418. ctx->sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, connect_host, 0, connect_port_str, &timeout);
  419. if (ctx->sock < 0) {
  420. error("Error connecting TCP socket to \"%s\"", connect_host);
  421. goto exit_buf_rx;
  422. }
  423. if (fcntl(ctx->sock, F_SETFL, fcntl(ctx->sock, F_GETFL, 0) | O_NONBLOCK) == -1) {
  424. error("Error setting O_NONBLOCK to TCP socket.");
  425. goto exit_sock;
  426. }
  427. ctx->poll_fd.fd = ctx->sock;
  428. // Do the CONNECT if proxy is used
  429. if (request->proxy_host) {
  430. https_req_t req = HTTPS_REQ_T_INITIALIZER;
  431. req.request_type = HTTP_REQ_CONNECT;
  432. req.timeout_s = request->timeout_s;
  433. req.host = request->host;
  434. req.port = request->port;
  435. req.url = request->url;
  436. req.proxy_username = request->proxy_username;
  437. req.proxy_password = request->proxy_password;
  438. ctx->request = &req;
  439. if (handle_http_request(ctx)) {
  440. error("Failed to CONNECT with proxy");
  441. goto exit_sock;
  442. }
  443. if (ctx->parse_ctx.http_code != 200) {
  444. error("Proxy didn't return 200 OK (got %d)", ctx->parse_ctx.http_code);
  445. goto exit_sock;
  446. }
  447. info("Proxy accepted CONNECT upgrade");
  448. }
  449. ctx->request = request;
  450. ctx->ssl_ctx = security_initialize_openssl_client();
  451. if (ctx->ssl_ctx==NULL) {
  452. error("Cannot allocate SSL context");
  453. goto exit_sock;
  454. }
  455. if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
  456. error("Error setting default verify paths");
  457. goto exit_CTX;
  458. }
  459. SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, cert_verify_callback);
  460. ctx->ssl = SSL_new(ctx->ssl_ctx);
  461. if (ctx->ssl==NULL) {
  462. error("Cannot allocate SSL");
  463. goto exit_CTX;
  464. }
  465. SSL_set_fd(ctx->ssl, ctx->sock);
  466. ret = SSL_connect(ctx->ssl);
  467. if (ret != -1 && ret != 1) {
  468. error("SSL could not connect");
  469. goto exit_SSL;
  470. }
  471. if (ret == -1) {
  472. // expected as underlying socket is non blocking!
  473. // consult SSL_connect documentation for details
  474. int ec = SSL_get_error(ctx->ssl, ret);
  475. if (ec != SSL_ERROR_WANT_READ && ec != SSL_ERROR_WANT_WRITE) {
  476. error("Failed to start SSL connection");
  477. goto exit_SSL;
  478. }
  479. }
  480. // The actual request here
  481. if (handle_http_request(ctx)) {
  482. error("Couldn't process request");
  483. goto exit_SSL;
  484. }
  485. response->http_code = ctx->parse_ctx.http_code;
  486. if (ctx->parse_ctx.content_length > 0) {
  487. response->payload_size = ctx->parse_ctx.content_length;
  488. response->payload = mallocz(response->payload_size + 1);
  489. ret = rbuf_pop(ctx->buf_rx, response->payload, response->payload_size);
  490. if (ret != (int)response->payload_size) {
  491. error("Payload size doesn't match remaining data on the buffer!");
  492. response->payload_size = ret;
  493. }
  494. // normally we take payload as it is and copy it
  495. // but for convenience in cases where payload is sth. like
  496. // json we add terminating zero so that user of the data
  497. // doesn't have to convert to C string (0 terminated)
  498. // other uses still have correct payload_size and can copy
  499. // only exact data without affixed 0x00
  500. ((char*)response->payload)[response->payload_size] = 0; // mallocz(response->payload_size + 1);
  501. }
  502. info("HTTPS \"%s\" request to \"%s\" finished with HTTP code: %d", http_req_type_to_str(ctx->request->request_type), ctx->request->host, response->http_code);
  503. rc = 0;
  504. exit_SSL:
  505. SSL_free(ctx->ssl);
  506. exit_CTX:
  507. SSL_CTX_free(ctx->ssl_ctx);
  508. exit_sock:
  509. close(ctx->sock);
  510. exit_buf_rx:
  511. rbuf_free(ctx->buf_rx);
  512. exit_req_ctx:
  513. freez(ctx);
  514. return rc;
  515. }
  516. void https_req_response_free(https_req_response_t *res) {
  517. freez(res->payload);
  518. }
  519. void https_req_response_init(https_req_response_t *res) {
  520. res->http_code = 0;
  521. res->payload = NULL;
  522. res->payload_size = 0;
  523. }
  524. static inline char *UNUSED_FUNCTION(min_non_null)(char *a, char *b) {
  525. if (!a)
  526. return b;
  527. if (!b)
  528. return a;
  529. return (a < b ? a : b);
  530. }
  531. #define URI_PROTO_SEPARATOR "://"
  532. #define URL_PARSER_LOG_PREFIX "url_parser "
  533. static int parse_host_port(url_t *url) {
  534. char *ptr = strrchr(url->host, ':');
  535. if (ptr) {
  536. size_t port_len = strlen(ptr + 1);
  537. if (!port_len) {
  538. error(URL_PARSER_LOG_PREFIX ": specified but no port number");
  539. return 1;
  540. }
  541. if (port_len > 5 /* MAX port length is 5digit long in decimal */) {
  542. error(URL_PARSER_LOG_PREFIX "port # is too long");
  543. return 1;
  544. }
  545. *ptr = 0;
  546. if (!strlen(url->host)) {
  547. error(URL_PARSER_LOG_PREFIX "host empty after removing port");
  548. return 1;
  549. }
  550. url->port = atoi (ptr + 1);
  551. }
  552. return 0;
  553. }
  554. static inline void port_by_proto(url_t *url) {
  555. if (url->port)
  556. return;
  557. if (!url->proto)
  558. return;
  559. if (!strcmp(url->proto, "http")) {
  560. url->port = 80;
  561. return;
  562. }
  563. if (!strcmp(url->proto, "https")) {
  564. url->port = 443;
  565. return;
  566. }
  567. }
  568. #define STRDUPZ_2PTR(dest, start, end) \
  569. { \
  570. dest = mallocz(1 + end - start); \
  571. memcpy(dest, start, end - start); \
  572. dest[end - start] = 0; \
  573. }
  574. int url_parse(const char *url, url_t *parsed) {
  575. const char *start = url;
  576. const char *end = strstr(url, URI_PROTO_SEPARATOR);
  577. if (end) {
  578. if (end == start) {
  579. error (URL_PARSER_LOG_PREFIX "found " URI_PROTO_SEPARATOR " without protocol specified");
  580. return 1;
  581. }
  582. STRDUPZ_2PTR(parsed->proto, start, end)
  583. start = end + strlen(URI_PROTO_SEPARATOR);
  584. }
  585. end = strchr(start, '/');
  586. if (!end)
  587. end = start + strlen(start);
  588. if (start == end) {
  589. error(URL_PARSER_LOG_PREFIX "Host empty");
  590. return 1;
  591. }
  592. STRDUPZ_2PTR(parsed->host, start, end);
  593. if (parse_host_port(parsed))
  594. return 1;
  595. if (!*end) {
  596. parsed->path = strdupz("/");
  597. port_by_proto(parsed);
  598. return 0;
  599. }
  600. parsed->path = strdupz(end);
  601. port_by_proto(parsed);
  602. return 0;
  603. }
  604. void url_t_destroy(url_t *url) {
  605. freez(url->host);
  606. freez(url->path);
  607. freez(url->proto);
  608. }