109 {
110 if (!hostname || !path) {
111 log_error("Invalid arguments to https_get");
112 return NULL;
113 }
114
115 log_info("HTTPS GET https://%s%s", hostname, path);
116
117
118 char *pem_data = NULL;
119 size_t pem_size = 0;
120 if (platform_load_system_ca_certs(&pem_data, &pem_size) != 0) {
121 log_error("Failed to load system CA certificates");
122 return NULL;
123 }
124
125
126 anchor_list anchors = ANCHOR_LIST_INIT;
128 SAFE_FREE(pem_data);
129
130 if (num_anchors == 0) {
131 log_error("No trust anchors loaded");
132
133 goto cleanup_anchors;
134 }
135 log_info("Loaded %zu trust anchors", num_anchors);
136
137
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;
143
144 if (getaddrinfo(hostname, "443", &hints, &result) != 0) {
145 log_error("Failed to resolve hostname: %s", hostname);
146 goto cleanup_anchors;
147 }
148
149
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;
155 }
156
157
158 if (connect(sock, result->ai_addr, (int)result->ai_addrlen) != 0) {
159 log_error("Failed to connect to %s:443", hostname);
160 socket_close(sock);
161 freeaddrinfo(result);
162 goto cleanup_anchors;
163 }
164
165 freeaddrinfo(result);
166
167 log_info("Connected to %s:443", hostname);
168
169
170 br_x509_minimal_context xc;
171 br_x509_minimal_init(&xc, &br_sha256_vtable, anchors.buf, anchors.ptr);
172
173
174 br_ssl_client_context sc;
175 br_ssl_client_init_full(&sc, &xc, anchors.buf, anchors.ptr);
176
177
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);
181
182
183 br_sslio_context ioc;
184 br_sslio_init(&ioc, &sc.eng, sock_read, &sock, sock_write, &sock);
185
186
187 br_ssl_client_reset(&sc, hostname, 0);
188
189 log_info("Starting TLS handshake with %s", hostname);
190
191
192 char request[BUFFER_SIZE_LARGE];
194 "GET %s HTTP/1.1\r\n"
195 "Host: %s\r\n"
196 "Connection: close\r\n"
197 "User-Agent: ascii-chat/" ASCII_CHAT_VERSION_STRING "\r\n"
198 "\r\n",
199 path, hostname);
200
201
202 if (br_sslio_write_all(&ioc, request, (size_t)request_len) != 0) {
203 log_error("Failed to send HTTP request");
204 SAFE_FREE(iobuf);
205 socket_close(sock);
206 goto cleanup_anchors;
207 }
208
209 br_sslio_flush(&ioc);
210 log_info("Sent HTTP request");
211
212
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 *);
217
218 while (1) {
219
220 if (response_len + 1024 > response_capacity) {
221 response_capacity *= 2;
222 response_buf = SAFE_REALLOC(response_buf, response_capacity, char *);
223 }
224
225
226 int n = br_sslio_read(&ioc, response_buf + response_len, response_capacity - response_len);
227 if (n < 0) {
228
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);
233 SAFE_FREE(iobuf);
234 socket_close(sock);
235 goto cleanup_anchors;
236 }
237
238 break;
239 }
240 if (n == 0) {
241 break;
242 }
243
244 response_len += (size_t)n;
245 }
246
247 response_buf[response_len] = '\0';
248 log_info("Received %zu bytes", response_len);
249
250
251 br_sslio_close(&ioc);
252 socket_close(sock);
253
254
255 asciichat_error_t status = check_http_status(response_buf);
256 if (status != ASCIICHAT_OK) {
257 SAFE_FREE(response_buf);
258 SAFE_FREE(iobuf);
259 goto cleanup_anchors;
260 }
261
262 char *body = extract_http_body(response_buf, response_len);
263 SAFE_FREE(response_buf);
264 SAFE_FREE(iobuf);
265
266
267 for (size_t i = 0; i < anchors.ptr; i++) {
269 }
270 SAFE_FREE(anchors.buf);
271
272 return body;
273
274cleanup_anchors:
275 for (size_t i = 0; i < anchors.ptr; i++) {
277 }
278 SAFE_FREE(anchors.buf);
279 return NULL;
280}
size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len)
void free_ta_contents(br_x509_trust_anchor *ta)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.