109char *
https_get(
const char *hostname,
const char *path) {
110 if (!hostname || !path) {
111 log_error(
"Invalid arguments to https_get");
115 log_info(
"HTTPS GET https://%s%s", hostname, path);
118 char *pem_data = NULL;
120 if (platform_load_system_ca_certs(&pem_data, &pem_size) != 0) {
121 log_error(
"Failed to load system CA certificates");
126 anchor_list anchors = ANCHOR_LIST_INIT;
130 if (num_anchors == 0) {
131 log_error(
"No trust anchors loaded");
133 goto cleanup_anchors;
135 log_info(
"Loaded %zu trust anchors", num_anchors);
138 struct addrinfo hints, *result;
139 memset(&hints, 0,
sizeof(hints));
140 hints.ai_family = AF_INET;
141 hints.ai_socktype = SOCK_STREAM;
142 hints.ai_protocol = IPPROTO_TCP;
144 if (getaddrinfo(hostname,
"443", &hints, &result) != 0) {
145 log_error(
"Failed to resolve hostname: %s", hostname);
146 goto cleanup_anchors;
150 socket_t sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
151 if (sock == INVALID_SOCKET_VALUE) {
152 log_error(
"Failed to create socket");
153 freeaddrinfo(result);
154 goto cleanup_anchors;
158 if (connect(sock, result->ai_addr, (
int)result->ai_addrlen) != 0) {
159 log_error(
"Failed to connect to %s:443", hostname);
161 freeaddrinfo(result);
162 goto cleanup_anchors;
165 freeaddrinfo(result);
167 log_info(
"Connected to %s:443", hostname);
170 br_x509_minimal_context xc;
171 br_x509_minimal_init(&xc, &br_sha256_vtable, anchors.buf, anchors.ptr);
174 br_ssl_client_context sc;
175 br_ssl_client_init_full(&sc, &xc, anchors.buf, anchors.ptr);
178 unsigned char *iobuf;
179 iobuf = SAFE_MALLOC(BR_SSL_BUFSIZE_BIDI,
unsigned char *);
180 br_ssl_engine_set_buffer(&sc.eng, iobuf, BR_SSL_BUFSIZE_BIDI, 1);
183 br_sslio_context ioc;
184 br_sslio_init(&ioc, &sc.eng, sock_read, &sock, sock_write, &sock);
187 br_ssl_client_reset(&sc, hostname, 0);
189 log_info(
"Starting TLS handshake with %s", hostname);
192 char request[BUFFER_SIZE_LARGE];
194 "GET %s HTTP/1.1\r\n"
196 "Connection: close\r\n"
197 "User-Agent: ascii-chat/" ASCII_CHAT_VERSION_STRING
"\r\n"
202 if (br_sslio_write_all(&ioc, request, (
size_t)request_len) != 0) {
203 log_error(
"Failed to send HTTP request");
206 goto cleanup_anchors;
209 br_sslio_flush(&ioc);
210 log_info(
"Sent HTTP request");
213 char *response_buf = NULL;
214 size_t response_capacity = 8192;
215 size_t response_len = 0;
216 response_buf = SAFE_MALLOC(response_capacity,
char *);
220 if (response_len + 1024 > response_capacity) {
221 response_capacity *= 2;
222 response_buf = SAFE_REALLOC(response_buf, response_capacity,
char *);
226 int n = br_sslio_read(&ioc, response_buf + response_len, response_capacity - response_len);
229 int err = br_ssl_engine_last_error(&sc.eng);
230 if (err != BR_ERR_OK) {
231 log_error(
"TLS error: %d", err);
232 SAFE_FREE(response_buf);
235 goto cleanup_anchors;
244 response_len += (size_t)n;
247 response_buf[response_len] =
'\0';
248 log_info(
"Received %zu bytes", response_len);
251 br_sslio_close(&ioc);
255 asciichat_error_t status = check_http_status(response_buf);
256 if (status != ASCIICHAT_OK) {
257 SAFE_FREE(response_buf);
259 goto cleanup_anchors;
262 char *body = extract_http_body(response_buf, response_len);
263 SAFE_FREE(response_buf);
267 for (
size_t i = 0; i < anchors.ptr; i++) {
270 SAFE_FREE(anchors.buf);
275 for (
size_t i = 0; i < anchors.ptr; i++) {
278 SAFE_FREE(anchors.buf);