Bug Summary

File:src/usr.sbin/rpki-client/http.c
Warning:line 402, column 7
Potential leak of memory pointed to by 'cred'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name http.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/rpki-client -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/rpki-client/http.c
1/* $OpenBSD: http.c,v 1.51 2021/12/22 09:35:14 claudio Exp $ */
2/*
3 * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
4 * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*-
20 * Copyright (c) 1997 The NetBSD Foundation, Inc.
21 * All rights reserved.
22 *
23 * This code is derived from software contributed to The NetBSD Foundation
24 * by Jason Thorpe and Luke Mewburn.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
36 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
37 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
39 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45 * POSSIBILITY OF SUCH DAMAGE.
46 */
47#include <sys/types.h>
48#include <sys/queue.h>
49#include <sys/socket.h>
50
51#include <assert.h>
52#include <ctype.h>
53#include <err.h>
54#include <errno(*__errno()).h>
55#include <limits.h>
56#include <netdb.h>
57#include <poll.h>
58#include <signal.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63#include <vis.h>
64#include <imsg.h>
65
66#include <tls.h>
67
68#include "extern.h"
69
70#define HTTP_USER_AGENT"OpenBSD rpki-client" "OpenBSD rpki-client"
71#define HTTP_BUF_SIZE(32 * 1024) (32 * 1024)
72#define HTTP_IDLE_TIMEOUT10 10
73#define HTTP_IO_TIMEOUT(3 * 60) (3 * 60)
74#define MAX_CONNECTIONS64 64
75#define MAX_CONTENTLEN(2 * 1024 * 1024 * 1024LL) (2 * 1024 * 1024 * 1024LL)
76#define NPFDS(64 + 1) (MAX_CONNECTIONS64 + 1)
77
78enum res {
79 DONE,
80 WANT_POLLIN,
81 WANT_POLLOUT,
82};
83
84enum http_state {
85 STATE_FREE,
86 STATE_CONNECT,
87 STATE_TLSCONNECT,
88 STATE_PROXY_REQUEST,
89 STATE_PROXY_STATUS,
90 STATE_PROXY_RESPONSE,
91 STATE_REQUEST,
92 STATE_RESPONSE_STATUS,
93 STATE_RESPONSE_HEADER,
94 STATE_RESPONSE_DATA,
95 STATE_RESPONSE_CHUNKED_HEADER,
96 STATE_RESPONSE_CHUNKED_TRAILER,
97 STATE_WRITE_DATA,
98 STATE_IDLE,
99 STATE_CLOSE,
100};
101
102struct http_proxy {
103 char *proxyhost;
104 char *proxyport;
105 char *proxyauth;
106} proxy;
107
108struct http_connection {
109 LIST_ENTRY(http_connection)struct { struct http_connection *le_next; struct http_connection
**le_prev; }
entry;
110 char *host;
111 char *port;
112 char *last_modified;
113 char *redir_uri;
114 struct http_request *req;
115 struct pollfd *pfd;
116 struct addrinfo *res0;
117 struct addrinfo *res;
118 struct tls *tls;
119 char *buf;
120 size_t bufsz;
121 size_t bufpos;
122 off_t iosz;
123 off_t totalsz;
124 time_t idle_time;
125 time_t io_time;
126 int status;
127 int fd;
128 int chunked;
129 int keep_alive;
130 short events;
131 enum http_state state;
132};
133
134LIST_HEAD(http_conn_list, http_connection)struct http_conn_list { struct http_connection *lh_first; };
135
136struct http_request {
137 TAILQ_ENTRY(http_request)struct { struct http_request *tqe_next; struct http_request *
*tqe_prev; }
entry;
138 char *uri;
139 char *modified_since;
140 char *host;
141 char *port;
142 const char *path; /* points into uri */
143 unsigned int id;
144 int outfd;
145 int redirect_loop;
146};
147
148TAILQ_HEAD(http_req_queue, http_request)struct http_req_queue { struct http_request *tqh_first; struct
http_request **tqh_last; }
;
149
150static struct http_conn_list active = LIST_HEAD_INITIALIZER(active){ ((void*)0) };
151static struct http_conn_list idle = LIST_HEAD_INITIALIZER(idle){ ((void*)0) };
152static struct http_req_queue queue = TAILQ_HEAD_INITIALIZER(queue){ ((void*)0), &(queue).tqh_first };
153static unsigned int http_conn_count;
154
155static struct msgbuf msgq;
156static struct sockaddr_storage http_bindaddr;
157static struct tls_config *tls_config;
158static uint8_t *tls_ca_mem;
159static size_t tls_ca_size;
160
161/* HTTP request API */
162static void http_req_new(unsigned int, char *, char *, int, int);
163static void http_req_free(struct http_request *);
164static void http_req_done(unsigned int, enum http_result, const char *);
165static void http_req_fail(unsigned int);
166static int http_req_schedule(struct http_request *);
167
168/* HTTP connection API */
169static void http_new(struct http_request *);
170static void http_free(struct http_connection *);
171
172static enum res http_done(struct http_connection *, enum http_result);
173static enum res http_failed(struct http_connection *);
174
175/* HTTP connection FSM functions */
176static void http_do(struct http_connection *,
177 enum res (*)(struct http_connection *));
178
179/* These functions can be used with http_do() */
180static enum res http_connect(struct http_connection *);
181static enum res http_request(struct http_connection *);
182static enum res http_close(struct http_connection *);
183static enum res http_handle(struct http_connection *);
184
185/* Internal state functions used by the above functions */
186static enum res http_finish_connect(struct http_connection *);
187static enum res proxy_connect(struct http_connection *);
188static enum res http_tls_connect(struct http_connection *);
189static enum res http_tls_handshake(struct http_connection *);
190static enum res http_read(struct http_connection *);
191static enum res http_write(struct http_connection *);
192static enum res proxy_read(struct http_connection *);
193static enum res proxy_write(struct http_connection *);
194static enum res data_write(struct http_connection *);
195
196/*
197 * Return a string that can be used in error message to identify the
198 * connection.
199 */
200static const char *
201http_info(const char *uri)
202{
203 static char buf[80];
204
205 if (strnvis(buf, uri, sizeof buf, VIS_SAFE0x20) >= (int)sizeof buf) {
206 /* overflow, add indicator */
207 memcpy(buf + sizeof buf - 4, "...", 4);
208 }
209
210 return buf;
211}
212
213/*
214 * Determine whether the character needs encoding, per RFC2396.
215 */
216static int
217to_encode(const char *c0)
218{
219 /* 2.4.3. Excluded US-ASCII Characters */
220 const char *excluded_chars =
221 " " /* space */
222 "<>#\"" /* delims (modulo "%", see below) */
223 "{}|\\^[]`" /* unwise */
224 ;
225 const unsigned char *c = (const unsigned char *)c0;
226
227 /*
228 * No corresponding graphic US-ASCII.
229 * Control characters and octets not used in US-ASCII.
230 */
231 return (iscntrl(*c) || !isascii(*c) ||
232
233 /*
234 * '%' is also reserved, if is not followed by two
235 * hexadecimal digits.
236 */
237 strchr(excluded_chars, *c) != NULL((void*)0) ||
238 (*c == '%' && (!isxdigit(c[1]) || !isxdigit(c[2]))));
239}
240
241/*
242 * Encode given URL, per RFC2396.
243 * Allocate and return string to the caller.
244 */
245static char *
246url_encode(const char *path)
247{
248 size_t i, length, new_length;
249 char *epath, *epathp;
250
251 length = new_length = strlen(path);
252
253 /*
254 * First pass:
255 * Count unsafe characters, and determine length of the
256 * final URL.
257 */
258 for (i = 0; i < length; i++)
259 if (to_encode(path + i))
260 new_length += 2;
261
262 epath = epathp = malloc(new_length + 1); /* One more for '\0'. */
263 if (epath == NULL((void*)0))
264 err(1, NULL((void*)0));
265
266 /*
267 * Second pass:
268 * Encode, and copy final URL.
269 */
270 for (i = 0; i < length; i++)
271 if (to_encode(path + i)) {
272 snprintf(epathp, 4, "%%" "%02x",
273 (unsigned char)path[i]);
274 epathp += 3;
275 } else
276 *(epathp++) = path[i];
277
278 *epathp = '\0';
279 return (epath);
280}
281
282static char
283hextochar(const char *str)
284{
285 unsigned char c, ret;
286
287 c = str[0];
288 ret = c;
289 if (isalpha(c))
290 ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
291 else
292 ret -= '0';
293 ret *= 16;
294
295 c = str[1];
296 ret += c;
297 if (isalpha(c))
298 ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
299 else
300 ret -= '0';
301 return ret;
302}
303
304static char *
305url_decode(const char *str)
306{
307 char *ret, c;
308 int i, reallen;
309
310 if (str == NULL((void*)0))
311 return NULL((void*)0);
312 if ((ret = malloc(strlen(str) + 1)) == NULL((void*)0))
313 err(1, "Can't allocate memory for URL decoding");
314 for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
315 c = str[i];
316 if (c == '+') {
317 *ret = ' ';
318 continue;
319 }
320 /*
321 * Cannot use strtol here because next char
322 * after %xx may be a digit.
323 */
324 if (c == '%' && isxdigit((unsigned char)str[i + 1]) &&
325 isxdigit((unsigned char)str[i + 2])) {
326 *ret = hextochar(&str[i + 1]);
327 i += 2;
328 continue;
329 }
330 *ret = c;
331 }
332 *ret = '\0';
333 return ret - reallen;
334}
335
336static char *
337recode_credentials(const char *userinfo)
338{
339 char *ui, *creds;
340 size_t ulen;
341
342 /* url-decode the user and pass */
343 ui = url_decode(userinfo);
344
345 ulen = strlen(ui);
346 if (base64_encode(ui, ulen, &creds) == -1)
347 errx(1, "error in base64 encoding");
348 free(ui);
349 return (creds);
350}
351
352/*
353 * Parse a proxy URI and split it up into host, port and userinfo.
354 */
355static void
356proxy_parse_uri(char *uri)
357{
358 char *host, *port = NULL((void*)0), *cred, *cookie = NULL((void*)0);
359
360 if (uri
11.1
'uri' is not equal to NULL
== NULL((void*)0))
12
Taking false branch
361 return;
362
363 if (strncasecmp(uri, "http://", 7) != 0)
13
Taking false branch
364 errx(1, "%s: http_proxy not using http schema", http_info(uri));
365
366 host = uri + 7;
367 if ((host = strndup(host, strcspn(host, "/"))) == NULL((void*)0))
14
Memory is allocated
15
Assuming the condition is false
16
Taking false branch
368 err(1, NULL((void*)0));
369
370 cred = host;
371 host = strchr(cred, '@');
372 if (host != NULL((void*)0))
17
Assuming 'host' is not equal to NULL
18
Taking true branch
373 *host++ = '\0';
374 else {
375 host = cred;
376 cred = NULL((void*)0);
377 }
378
379 if (*host == '[') {
19
Assuming the condition is false
20
Taking false branch
380 char *hosttail;
381
382 if ((hosttail = strrchr(host, ']')) == NULL((void*)0))
383 errx(1, "%s: unmatched opening bracket",
384 http_info(uri));
385 if (hosttail[1] == '\0' || hosttail[1] == ':')
386 host++;
387 if (hosttail[1] == ':')
388 port = hosttail + 2;
389 *hosttail = '\0';
390 } else {
391 if ((port = strrchr(host, ':')) != NULL((void*)0))
21
Assuming the condition is false
22
Taking false branch
392 *port++ = '\0';
393 }
394
395 if (port
22.1
'port' is equal to NULL
== NULL((void*)0))
23
Taking true branch
396 port = "443";
397
398 if (cred
23.1
'cred' is not equal to NULL
!= NULL((void*)0)) {
24
Taking true branch
399 if (strchr(cred, ':') == NULL((void*)0))
25
Assuming the condition is false
26
Taking false branch
400 errx(1, "%s: malformed proxy url", http_info(uri));
401 cred = recode_credentials(cred);
402 if (asprintf(&cookie, "Proxy-Authorization: Basic %s\r\n",
27
Potential leak of memory pointed to by 'cred'
403 cred) == -1)
404 err(1, NULL((void*)0));
405 free(cred);
406 } else
407 if ((cookie = strdup("")) == NULL((void*)0))
408 err(1, NULL((void*)0));
409
410 proxy.proxyhost = host;
411 proxy.proxyport = port;
412 proxy.proxyauth = cookie;
413}
414
415/*
416 * Parse a URI and split it up into host, port and path.
417 * Does some basic URI validation. Both host and port need to be freed
418 * by the caller whereas path points into the uri.
419 */
420static int
421http_parse_uri(char *uri, char **ohost, char **oport, char **opath)
422{
423 char *host, *port = NULL((void*)0), *path;
424 char *hosttail;
425
426 if (strncasecmp(uri, "https://", 8) != 0) {
427 warnx("%s: not using https schema", http_info(uri));
428 return -1;
429 }
430 host = uri + 8;
431 if ((path = strchr(host, '/')) == NULL((void*)0)) {
432 warnx("%s: missing https path", http_info(uri));
433 return -1;
434 }
435 if (path - uri > INT_MAX2147483647 - 1) {
436 warnx("%s: preposterous host length", http_info(uri));
437 return -1;
438 }
439
440 if (memchr(host, '@', path - host) != NULL((void*)0)) {
441 warnx("%s: URI with userinfo not supported", http_info(uri));
442 return -1;
443 }
444
445 if (*host == '[') {
446 if ((hosttail = memrchr(host, ']', path - host)) == NULL((void*)0)) {
447 warnx("%s: unmatched opening bracket", http_info(uri));
448 return -1;
449 }
450 if (hosttail[1] == '/' || hosttail[1] == ':')
451 host++;
452 if (hosttail[1] == ':')
453 port = hosttail + 2;
454 } else {
455 if ((hosttail = memrchr(host, ':', path - host)) != NULL((void*)0))
456 port = hosttail + 1;
457 else
458 hosttail = path;
459 }
460
461 if ((host = strndup(host, hosttail - host)) == NULL((void*)0))
462 err(1, NULL((void*)0));
463 if (port != NULL((void*)0)) {
464 if ((port = strndup(port, path - port)) == NULL((void*)0))
465 err(1, NULL((void*)0));
466 } else {
467 if ((port = strdup("443")) == NULL((void*)0))
468 err(1, NULL((void*)0));
469 }
470 /* do not include the initial / in path */
471 path++;
472
473 *ohost = host;
474 *oport = port;
475 *opath = path;
476
477 return 0;
478}
479
480/*
481 * Lookup the IP addresses for host:port.
482 * Returns 0 on success and -1 on failure.
483 */
484static int
485http_resolv(struct addrinfo **res, const char *host, const char *port)
486{
487 struct addrinfo hints;
488 int error;
489
490 memset(&hints, 0, sizeof(hints));
491 hints.ai_family = PF_UNSPEC0;
492 hints.ai_socktype = SOCK_STREAM1;
493 error = getaddrinfo(host, port, &hints, res);
494 /*
495 * If the services file is corrupt/missing, fall back
496 * on our hard-coded defines.
497 */
498 if (error == EAI_SERVICE-8)
499 error = getaddrinfo(host, "443", &hints, res);
500 if (error != 0) {
501 warnx("%s: %s", host, gai_strerror(error));
502 return -1;
503 }
504
505 return 0;
506}
507
508/*
509 * Create and queue a new request.
510 */
511static void
512http_req_new(unsigned int id, char *uri, char *modified_since, int count,
513 int outfd)
514{
515 struct http_request *req;
516 char *host, *port, *path;
517
518 if (http_parse_uri(uri, &host, &port, &path) == -1) {
519 free(uri);
520 free(modified_since);
521 close(outfd);
522 http_req_fail(id);
523 return;
524 }
525
526 if ((req = calloc(1, sizeof(*req))) == NULL((void*)0))
527 err(1, NULL((void*)0));
528
529 req->id = id;
530 req->outfd = outfd;
531 req->host = host;
532 req->port = port;
533 req->path = path;
534 req->uri = uri;
535 req->modified_since = modified_since;
536 req->redirect_loop = count;
537
538 TAILQ_INSERT_TAIL(&queue, req, entry)do { (req)->entry.tqe_next = ((void*)0); (req)->entry.tqe_prev
= (&queue)->tqh_last; *(&queue)->tqh_last = (req
); (&queue)->tqh_last = &(req)->entry.tqe_next;
} while (0)
;
539}
540
541/*
542 * Free a request, request is not allowed to be on the req queue.
543 */
544static void
545http_req_free(struct http_request *req)
546{
547 if (req == NULL((void*)0))
548 return;
549
550 free(req->host);
551 free(req->port);
552 /* no need to free req->path it points into req->uri */
553 free(req->uri);
554 free(req->modified_since);
555
556 if (req->outfd != -1)
557 close(req->outfd);
558}
559
560/*
561 * Enqueue request response
562 */
563static void
564http_req_done(unsigned int id, enum http_result res, const char *last_modified)
565{
566 struct ibuf *b;
567
568 b = io_new_buffer();
569 io_simple_buffer(b, &id, sizeof(id));
570 io_simple_buffer(b, &res, sizeof(res));
571 io_str_buffer(b, last_modified);
572 io_close_buffer(&msgq, b);
573}
574
575/*
576 * Enqueue request failure response
577 */
578static void
579http_req_fail(unsigned int id)
580{
581 struct ibuf *b;
582 enum http_result res = HTTP_FAILED;
583
584 b = io_new_buffer();
585 io_simple_buffer(b, &id, sizeof(id));
586 io_simple_buffer(b, &res, sizeof(res));
587 io_str_buffer(b, NULL((void*)0));
588 io_close_buffer(&msgq, b);
589}
590
591/*
592 * Schedule new requests until maximum number of connections is reached.
593 * Try to reuse an idle connection if one exists that matches host and port.
594 */
595static int
596http_req_schedule(struct http_request *req)
597{
598 struct http_connection *conn;
599
600 TAILQ_REMOVE(&queue, req, entry)do { if (((req)->entry.tqe_next) != ((void*)0)) (req)->
entry.tqe_next->entry.tqe_prev = (req)->entry.tqe_prev;
else (&queue)->tqh_last = (req)->entry.tqe_prev; *
(req)->entry.tqe_prev = (req)->entry.tqe_next; ; ; } while
(0)
;
601
602 /* check list of idle connections first */
603 LIST_FOREACH(conn, &idle, entry)for((conn) = ((&idle)->lh_first); (conn)!= ((void*)0);
(conn) = ((conn)->entry.le_next))
{
604 if (strcmp(conn->host, req->host) != 0)
605 continue;
606 if (strcmp(conn->port, req->port) != 0)
607 continue;
608
609 LIST_REMOVE(conn, entry)do { if ((conn)->entry.le_next != ((void*)0)) (conn)->entry
.le_next->entry.le_prev = (conn)->entry.le_prev; *(conn
)->entry.le_prev = (conn)->entry.le_next; ; ; } while (
0)
;
610 LIST_INSERT_HEAD(&active, conn, entry)do { if (((conn)->entry.le_next = (&active)->lh_first
) != ((void*)0)) (&active)->lh_first->entry.le_prev
= &(conn)->entry.le_next; (&active)->lh_first =
(conn); (conn)->entry.le_prev = &(&active)->lh_first
; } while (0)
;
611
612 /* use established connection */
613 conn->req = req;
614 conn->idle_time = 0;
615
616 /* start request */
617 http_do(conn, http_request);
618 if (conn->state == STATE_FREE)
619 http_free(conn);
620 return 1;
621 }
622
623 if (http_conn_count < MAX_CONNECTIONS64) {
624 http_new(req);
625 return 1;
626 }
627
628 /* no more slots free, requeue */
629 TAILQ_INSERT_HEAD(&queue, req, entry)do { if (((req)->entry.tqe_next = (&queue)->tqh_first
) != ((void*)0)) (&queue)->tqh_first->entry.tqe_prev
= &(req)->entry.tqe_next; else (&queue)->tqh_last
= &(req)->entry.tqe_next; (&queue)->tqh_first =
(req); (req)->entry.tqe_prev = &(&queue)->tqh_first
; } while (0)
;
630 return 0;
631}
632
633/*
634 * Create a new HTTP connection which will be used for the HTTP request req.
635 * On errors a req faulure is issued and both connection and request are freed.
636 */
637static void
638http_new(struct http_request *req)
639{
640 struct http_connection *conn;
641
642 if ((conn = calloc(1, sizeof(*conn))) == NULL((void*)0))
643 err(1, NULL((void*)0));
644
645 conn->fd = -1;
646 conn->req = req;
647 if ((conn->host = strdup(req->host)) == NULL((void*)0))
648 err(1, NULL((void*)0));
649 if ((conn->port = strdup(req->port)) == NULL((void*)0))
650 err(1, NULL((void*)0));
651
652 LIST_INSERT_HEAD(&active, conn, entry)do { if (((conn)->entry.le_next = (&active)->lh_first
) != ((void*)0)) (&active)->lh_first->entry.le_prev
= &(conn)->entry.le_next; (&active)->lh_first =
(conn); (conn)->entry.le_prev = &(&active)->lh_first
; } while (0)
;
653 http_conn_count++;
654
655 if (proxy.proxyhost != NULL((void*)0)) {
656 if (http_resolv(&conn->res0, proxy.proxyhost,
657 proxy.proxyport) == -1) {
658 http_req_fail(req->id);
659 http_free(conn);
660 return;
661 }
662 } else {
663 if (http_resolv(&conn->res0, conn->host, conn->port) == -1) {
664 http_req_fail(req->id);
665 http_free(conn);
666 return;
667 }
668 }
669
670 /* connect and start request */
671 http_do(conn, http_connect);
672 if (conn->state == STATE_FREE)
673 http_free(conn);
674}
675
676/*
677 * Free a no longer active connection, releasing all memory and closing
678 * any open file descriptor.
679 */
680static void
681http_free(struct http_connection *conn)
682{
683 assert(conn->state == STATE_FREE)((conn->state == STATE_FREE) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 683, __func__, "conn->state == STATE_FREE"))
;
684
685 LIST_REMOVE(conn, entry)do { if ((conn)->entry.le_next != ((void*)0)) (conn)->entry
.le_next->entry.le_prev = (conn)->entry.le_prev; *(conn
)->entry.le_prev = (conn)->entry.le_next; ; ; } while (
0)
;
686 http_conn_count--;
687
688 http_req_free(conn->req);
689 free(conn->host);
690 free(conn->port);
691 free(conn->last_modified);
692 free(conn->redir_uri);
693 free(conn->buf);
694
695 if (conn->res0 != NULL((void*)0))
696 freeaddrinfo(conn->res0);
697
698 tls_free(conn->tls);
699
700 if (conn->fd != -1)
701 close(conn->fd);
702 free(conn);
703}
704
705/*
706 * Called when a request on this connection is finished.
707 * Move connection into idle state and onto idle queue.
708 * If there is a request connected to it send back a response
709 * with http_result res, else ignore the res.
710 */
711static enum res
712http_done(struct http_connection *conn, enum http_result res)
713{
714 assert(conn->bufpos == 0)((conn->bufpos == 0) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 714, __func__, "conn->bufpos == 0"))
;
715 assert(conn->iosz == 0)((conn->iosz == 0) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 715, __func__, "conn->iosz == 0"))
;
716 assert(conn->chunked == 0)((conn->chunked == 0) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 716, __func__, "conn->chunked == 0"))
;
717 assert(conn->redir_uri == NULL)((conn->redir_uri == ((void*)0)) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 717, __func__, "conn->redir_uri == NULL"))
;
718
719 conn->state = STATE_IDLE;
720 conn->idle_time = getmonotime() + HTTP_IDLE_TIMEOUT10;
721
722 if (conn->req) {
723 http_req_done(conn->req->id, res, conn->last_modified);
724 http_req_free(conn->req);
725 conn->req = NULL((void*)0);
726 }
727
728 if (!conn->keep_alive)
729 return http_close(conn);
730
731 LIST_REMOVE(conn, entry)do { if ((conn)->entry.le_next != ((void*)0)) (conn)->entry
.le_next->entry.le_prev = (conn)->entry.le_prev; *(conn
)->entry.le_prev = (conn)->entry.le_next; ; ; } while (
0)
;
732 LIST_INSERT_HEAD(&idle, conn, entry)do { if (((conn)->entry.le_next = (&idle)->lh_first
) != ((void*)0)) (&idle)->lh_first->entry.le_prev =
&(conn)->entry.le_next; (&idle)->lh_first = (conn
); (conn)->entry.le_prev = &(&idle)->lh_first; }
while (0)
;
733
734 /* reset status and keep-alive for good measures */
735 conn->status = 0;
736 conn->keep_alive = 0;
737
738 return WANT_POLLIN;
739}
740
741/*
742 * Called in case of error, moves connection into free state.
743 * This will skip proper shutdown of the TLS session.
744 * If a request is pending fail and free the request.
745 */
746static enum res
747http_failed(struct http_connection *conn)
748{
749 conn->state = STATE_FREE;
750
751 if (conn->req) {
752 http_req_fail(conn->req->id);
753 http_req_free(conn->req);
754 conn->req = NULL((void*)0);
755 }
756
757 return DONE;
758}
759
760/*
761 * Call the function f and update the connection events based
762 * on the return value.
763 */
764static void
765http_do(struct http_connection *conn, enum res (*f)(struct http_connection *))
766{
767 switch (f(conn)) {
768 case DONE:
769 conn->events = 0;
770 break;
771 case WANT_POLLIN:
772 conn->events = POLLIN0x0001;
773 break;
774 case WANT_POLLOUT:
775 conn->events = POLLOUT0x0004;
776 break;
777 default:
778 errx(1, "%s: unexpected function return",
779 http_info(conn->host));
780 }
781}
782
783/*
784 * Connection successfully establish, initiate TLS handshake or proxy request.
785 */
786static enum res
787http_connect_done(struct http_connection *conn)
788{
789 freeaddrinfo(conn->res0);
790 conn->res0 = NULL((void*)0);
791 conn->res = NULL((void*)0);
792
793 if (proxy.proxyhost != NULL((void*)0))
794 return proxy_connect(conn);
795 return http_tls_connect(conn);
796}
797
798/*
799 * Start an asynchronous connect.
800 */
801static enum res
802http_connect(struct http_connection *conn)
803{
804 const char *cause = NULL((void*)0);
805
806 assert(conn->fd == -1)((conn->fd == -1) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 806, __func__, "conn->fd == -1"))
;
807 conn->state = STATE_CONNECT;
808
809 /* start the loop below with first or next address */
810 if (conn->res == NULL((void*)0))
811 conn->res = conn->res0;
812 else
813 conn->res = conn->res->ai_next;
814 for (; conn->res != NULL((void*)0); conn->res = conn->res->ai_next) {
815 struct addrinfo *res = conn->res;
816 int fd, save_errno;
817
818 fd = socket(res->ai_family,
819 res->ai_socktype | SOCK_NONBLOCK0x4000, res->ai_protocol);
820 if (fd == -1) {
821 cause = "socket";
822 continue;
823 }
824 conn->fd = fd;
825
826 if (http_bindaddr.ss_family == res->ai_family) {
827 if (bind(conn->fd, (struct sockaddr *)&http_bindaddr,
828 res->ai_addrlen) == -1) {
829 save_errno = errno(*__errno());
830 close(conn->fd);
831 conn->fd = -1;
832 errno(*__errno()) = save_errno;
833 cause = "bind";
834 continue;
835 }
836 }
837
838 if (connect(conn->fd, res->ai_addr, res->ai_addrlen) == -1) {
839 if (errno(*__errno()) == EINPROGRESS36) {
840 /* wait for async connect to finish. */
841 return WANT_POLLOUT;
842 } else {
843 save_errno = errno(*__errno());
844 close(conn->fd);
845 conn->fd = -1;
846 errno(*__errno()) = save_errno;
847 cause = "connect";
848 continue;
849 }
850 }
851
852 break; /* okay we got one */
853 }
854
855 if (conn->fd == -1) {
856 if (cause != NULL((void*)0))
857 warn("%s: %s", http_info(conn->req->uri), cause);
858 return http_failed(conn);
859 }
860
861 return http_connect_done(conn);
862}
863
864/*
865 * Called once an asynchronus connect request finished.
866 */
867static enum res
868http_finish_connect(struct http_connection *conn)
869{
870 int error = 0;
871 socklen_t len;
872
873 len = sizeof(error);
874 if (getsockopt(conn->fd, SOL_SOCKET0xffff, SO_ERROR0x1007, &error, &len) == -1) {
875 warn("%s: getsockopt SO_ERROR", http_info(conn->req->uri));
876 goto fail;
877 }
878 if (error != 0) {
879 errno(*__errno()) = error;
880 warn("%s: connect", http_info(conn->req->uri));
881 goto fail;
882 }
883
884 return http_connect_done(conn);
885
886fail:
887 close(conn->fd);
888 conn->fd = -1;
889
890 return http_connect(conn);
891}
892
893/*
894 * Initiate TLS session on a new connection.
895 */
896static enum res
897http_tls_connect(struct http_connection *conn)
898{
899 assert(conn->state == STATE_CONNECT)((conn->state == STATE_CONNECT) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 899, __func__, "conn->state == STATE_CONNECT"))
;
900 conn->state = STATE_TLSCONNECT;
901
902 if ((conn->tls = tls_client()) == NULL((void*)0)) {
903 warn("tls_client");
904 return http_failed(conn);
905 }
906 if (tls_configure(conn->tls, tls_config) == -1) {
907 warnx("%s: TLS configuration: %s\n", http_info(conn->req->uri),
908 tls_error(conn->tls));
909 return http_failed(conn);
910 }
911 if (tls_connect_socket(conn->tls, conn->fd, conn->host) == -1) {
912 warnx("%s: TLS connect: %s\n", http_info(conn->req->uri),
913 tls_error(conn->tls));
914 return http_failed(conn);
915 }
916
917 return http_tls_handshake(conn);
918}
919
920/*
921 * Do the tls_handshake and then send out the HTTP request.
922 */
923static enum res
924http_tls_handshake(struct http_connection *conn)
925{
926 switch (tls_handshake(conn->tls)) {
927 case -1:
928 warnx("%s: TLS handshake: %s", http_info(conn->req->uri),
929 tls_error(conn->tls));
930 return http_failed(conn);
931 case TLS_WANT_POLLIN-2:
932 return WANT_POLLIN;
933 case TLS_WANT_POLLOUT-3:
934 return WANT_POLLOUT;
935 }
936
937 return http_request(conn);
938}
939
940static enum res
941proxy_connect(struct http_connection *conn)
942{
943 char *host;
944 int r;
945
946 assert(conn->state == STATE_CONNECT)((conn->state == STATE_CONNECT) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 946, __func__, "conn->state == STATE_CONNECT"))
;
947 conn->state = STATE_PROXY_REQUEST;
948
949 /* Construct the Host header from host and port info */
950 if (strchr(conn->host, ':')) {
951 if (asprintf(&host, "[%s]:%s", conn->host, conn->port) == -1)
952 err(1, NULL((void*)0));
953
954 } else {
955 if (asprintf(&host, "%s:%s", conn->host, conn->port) == -1)
956 err(1, NULL((void*)0));
957 }
958
959 free(conn->buf);
960 conn->bufpos = 0;
961 /* XXX handle auth */
962 if ((r = asprintf(&conn->buf, "CONNECT %s HTTP/1.1\r\n"
963 "User-Agent: " HTTP_USER_AGENT"OpenBSD rpki-client" "\r\n%s\r\n", host,
964 proxy.proxyauth)) == -1)
965 err(1, NULL((void*)0));
966 conn->bufsz = r;
967
968 free(host);
969
970 return proxy_write(conn);
971}
972
973/*
974 * Build the HTTP request and send it out.
975 */
976static enum res
977http_request(struct http_connection *conn)
978{
979 char *host, *epath, *modified_since;
980 int r, with_port = 0;
981
982 assert(conn->state == STATE_IDLE || conn->state == STATE_TLSCONNECT)((conn->state == STATE_IDLE || conn->state == STATE_TLSCONNECT
) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 982, __func__, "conn->state == STATE_IDLE || conn->state == STATE_TLSCONNECT"
))
;
983 conn->state = STATE_REQUEST;
984
985 /*
986 * Send port number only if it's specified and does not equal
987 * the default. Some broken HTTP servers get confused if you explicitly
988 * send them the port number.
989 */
990 if (strcmp(conn->port, "443") != 0)
991 with_port = 1;
992
993 /* Construct the Host header from host and port info */
994 if (strchr(conn->host, ':')) {
995 if (asprintf(&host, "[%s]%s%s", conn->host,
996 with_port ? ":" : "", with_port ? conn->port : "") == -1)
997 err(1, NULL((void*)0));
998
999 } else {
1000 if (asprintf(&host, "%s%s%s", conn->host,
1001 with_port ? ":" : "", with_port ? conn->port : "") == -1)
1002 err(1, NULL((void*)0));
1003 }
1004
1005 /*
1006 * Construct and send the request. Proxy requests don't want leading /.
1007 */
1008 epath = url_encode(conn->req->path);
1009
1010 modified_since = NULL((void*)0);
1011 if (conn->req->modified_since != NULL((void*)0)) {
1012 if (asprintf(&modified_since, "If-Modified-Since: %s\r\n",
1013 conn->req->modified_since) == -1)
1014 err(1, NULL((void*)0));
1015 }
1016
1017 free(conn->buf);
1018 conn->bufpos = 0;
1019 if ((r = asprintf(&conn->buf,
1020 "GET /%s HTTP/1.1\r\n"
1021 "Host: %s\r\n"
1022 "Accept-Encoding: identity\r\n"
1023 "User-Agent: " HTTP_USER_AGENT"OpenBSD rpki-client" "\r\n"
1024 "%s\r\n",
1025 epath, host,
1026 modified_since ? modified_since : "")) == -1)
1027 err(1, NULL((void*)0));
1028 conn->bufsz = r;
1029
1030 free(epath);
1031 free(host);
1032 free(modified_since);
1033
1034 return http_write(conn);
1035}
1036
1037/*
1038 * Parse the HTTP status line.
1039 * Return 0 for status codes 100, 103, 200, 203, 301-304, 307-308.
1040 * The other 1xx and 2xx status codes are explicitly not handled and are
1041 * considered an error.
1042 * Failure codes and other errors return -1.
1043 * The redirect loop limit is enforced here.
1044 */
1045static int
1046http_parse_status(struct http_connection *conn, char *buf)
1047{
1048#define HTTP_11"HTTP/1.1 " "HTTP/1.1 "
1049 const char *errstr;
1050 char *cp, ststr[4];
1051 char gerror[200];
1052 int status;
1053
1054 /* Check if the protocol is 1.1 and enable keep-alive in that case */
1055 if (strncmp(buf, HTTP_11"HTTP/1.1 ", strlen(HTTP_11"HTTP/1.1 ")) == 0)
1056 conn->keep_alive = 1;
1057
1058 cp = strchr(buf, ' ');
1059 if (cp == NULL((void*)0)) {
1060 warnx("Improper response from %s", http_info(conn->host));
1061 return -1;
1062 } else
1063 cp++;
1064
1065 strlcpy(ststr, cp, sizeof(ststr));
1066 status = strtonum(ststr, 100, 599, &errstr);
1067 if (errstr != NULL((void*)0)) {
1068 strnvis(gerror, cp, sizeof gerror, VIS_SAFE0x20);
1069 warnx("Error retrieving %s: %s", http_info(conn->host),
1070 gerror);
1071 return -1;
1072 }
1073
1074 switch (status) {
1075 case 301: /* Redirect: moved permanently */
1076 case 302: /* Redirect: found / moved temporarily */
1077 case 303: /* Redirect: see other */
1078 case 307: /* Redirect: temporary redirect */
1079 case 308: /* Redirect: permanent redirect */
1080 if (conn->req->redirect_loop++ > 10) {
1081 warnx("%s: Too many redirections requested",
1082 http_info(conn->host));
1083 return -1;
1084 }
1085 /* FALLTHROUGH */
1086 case 100: /* Informational: continue (ignored) */
1087 case 103: /* Informational: early hints (ignored) */
1088 /* FALLTHROUGH */
1089 case 200: /* Success: OK */
1090 case 203: /* Success: non-authoritative information (proxy) */
1091 case 304: /* Redirect: not modified */
1092 conn->status = status;
1093 break;
1094 default:
1095 strnvis(gerror, cp, sizeof gerror, VIS_SAFE0x20);
1096 warnx("Error retrieving %s: %s", http_info(conn->host),
1097 gerror);
1098 return -1;
1099 }
1100
1101 return 0;
1102}
1103
1104/*
1105 * Returns true if the connection status is any of the redirect codes.
1106 */
1107static inline int
1108http_isredirect(struct http_connection *conn)
1109{
1110 if ((conn->status >= 301 && conn->status <= 303) ||
1111 conn->status == 307 || conn->status == 308)
1112 return 1;
1113 return 0;
1114}
1115
1116static inline int
1117http_isok(struct http_connection *conn)
1118{
1119 if (conn->status >= 200 && conn->status < 300)
1120 return 1;
1121 return 0;
1122}
1123
1124static void
1125http_redirect(struct http_connection *conn)
1126{
1127 char *uri, *mod_since = NULL((void*)0);
1128 int outfd;
1129
1130 /* move uri and fd out for new request */
1131 outfd = conn->req->outfd;
1132 conn->req->outfd = -1;
1133
1134 uri = conn->redir_uri;
1135 conn->redir_uri = NULL((void*)0);
1136
1137 if (conn->req->modified_since)
1138 if ((mod_since = strdup(conn->req->modified_since)) == NULL((void*)0))
1139 err(1, NULL((void*)0));
1140
1141 logx("redirect to %s", http_info(uri));
1142 http_req_new(conn->req->id, uri, mod_since, conn->req->redirect_loop,
1143 outfd);
1144
1145 /* clear request before moving connection to idle */
1146 http_req_free(conn->req);
1147 conn->req = NULL((void*)0);
1148}
1149
1150static int
1151http_parse_header(struct http_connection *conn, char *buf)
1152{
1153#define CONTENTLEN"Content-Length: " "Content-Length: "
1154#define LOCATION"Location: " "Location: "
1155#define CONNECTION"Connection: " "Connection: "
1156#define TRANSFER_ENCODING"Transfer-Encoding: " "Transfer-Encoding: "
1157#define LAST_MODIFIED"Last-Modified: " "Last-Modified: "
1158 const char *errstr;
1159 char *cp, *redirurl;
1160 char *locbase, *loctail;
1161
1162 cp = buf;
1163 /* empty line, end of header */
1164 if (*cp == '\0')
1165 return 0;
1166 else if (strncasecmp(cp, CONTENTLEN"Content-Length: ", sizeof(CONTENTLEN"Content-Length: ") - 1) == 0) {
1167 size_t s;
1168 cp += sizeof(CONTENTLEN"Content-Length: ") - 1;
1169 if ((s = strcspn(cp, " \t")) != 0)
1170 *(cp+s) = 0;
1171 conn->iosz = strtonum(cp, 0, MAX_CONTENTLEN(2 * 1024 * 1024 * 1024LL), &errstr);
1172 if (errstr != NULL((void*)0)) {
1173 warnx("Content-Length of %s is %s",
1174 http_info(conn->req->uri), errstr);
1175 return -1;
1176 }
1177 } else if (http_isredirect(conn) &&
1178 strncasecmp(cp, LOCATION"Location: ", sizeof(LOCATION"Location: ") - 1) == 0) {
1179 cp += sizeof(LOCATION"Location: ") - 1;
1180 /*
1181 * If there is a colon before the first slash, this URI
1182 * is not relative. RFC 3986 4.2
1183 */
1184 if (cp[strcspn(cp, ":/")] != ':') {
1185 /* XXX doesn't handle protocol-relative URIs */
1186 if (*cp == '/') {
1187 locbase = NULL((void*)0);
1188 cp++;
1189 } else {
1190 locbase = strdup(conn->req->path);
1191 if (locbase == NULL((void*)0))
1192 err(1, NULL((void*)0));
1193 loctail = strchr(locbase, '#');
1194 if (loctail != NULL((void*)0))
1195 *loctail = '\0';
1196 loctail = strchr(locbase, '?');
1197 if (loctail != NULL((void*)0))
1198 *loctail = '\0';
1199 loctail = strrchr(locbase, '/');
1200 if (loctail == NULL((void*)0)) {
1201 free(locbase);
1202 locbase = NULL((void*)0);
1203 } else
1204 loctail[1] = '\0';
1205 }
1206 /* Construct URL from relative redirect */
1207 if (asprintf(&redirurl, "%.*s/%s%s",
1208 (int)(conn->req->path - conn->req->uri),
1209 conn->req->uri, locbase ? locbase : "", cp) == -1)
1210 err(1, "Cannot build redirect URL");
1211 free(locbase);
1212 } else if ((redirurl = strdup(cp)) == NULL((void*)0))
1213 err(1, "Cannot build redirect URL");
1214 loctail = strchr(redirurl, '#');
1215 if (loctail != NULL((void*)0))
1216 *loctail = '\0';
1217 conn->redir_uri = redirurl;
1218 } else if (strncasecmp(cp, TRANSFER_ENCODING"Transfer-Encoding: ",
1219 sizeof(TRANSFER_ENCODING"Transfer-Encoding: ") - 1) == 0) {
1220 cp += sizeof(TRANSFER_ENCODING"Transfer-Encoding: ") - 1;
1221 cp[strcspn(cp, " \t")] = '\0';
1222 if (strcasecmp(cp, "chunked") == 0)
1223 conn->chunked = 1;
1224 } else if (strncasecmp(cp, CONNECTION"Connection: ", sizeof(CONNECTION"Connection: ") - 1) == 0) {
1225 cp += sizeof(CONNECTION"Connection: ") - 1;
1226 cp[strcspn(cp, " \t")] = '\0';
1227 if (strcasecmp(cp, "close") == 0)
1228 conn->keep_alive = 0;
1229 else if (strcasecmp(cp, "keep-alive") == 0)
1230 conn->keep_alive = 1;
1231 } else if (strncasecmp(cp, LAST_MODIFIED"Last-Modified: ",
1232 sizeof(LAST_MODIFIED"Last-Modified: ") - 1) == 0) {
1233 cp += sizeof(LAST_MODIFIED"Last-Modified: ") - 1;
1234 if ((conn->last_modified = strdup(cp)) == NULL((void*)0))
1235 err(1, NULL((void*)0));
1236 }
1237
1238 return 1;
1239}
1240
1241/*
1242 * Return one line from the HTTP response.
1243 * The line returned has any possible '\r' and '\n' at the end stripped.
1244 * The buffer is advanced to the start of the next line.
1245 * If there is currently no full line in the buffer NULL is returned.
1246 */
1247static char *
1248http_get_line(struct http_connection *conn)
1249{
1250 char *end, *line;
1251 size_t len;
1252
1253 end = memchr(conn->buf, '\n', conn->bufpos);
1254 if (end == NULL((void*)0))
1255 return NULL((void*)0);
1256
1257 len = end - conn->buf;
1258 while (len > 0 && conn->buf[len - 1] == '\r')
1259 --len;
1260 if ((line = strndup(conn->buf, len)) == NULL((void*)0))
1261 err(1, NULL((void*)0));
1262
1263 /* consume line including \n */
1264 end++;
1265 conn->bufpos -= end - conn->buf;
1266 memmove(conn->buf, end, conn->bufpos);
1267
1268 return line;
1269}
1270
1271/*
1272 * Parse the header between data chunks during chunked transfers.
1273 * Returns 0 if a new chunk size could be correctly read.
1274 * Returns 1 for the empty trailer lines.
1275 * If the chuck size could not be converted properly -1 is returned.
1276 */
1277static int
1278http_parse_chunked(struct http_connection *conn, char *buf)
1279{
1280 char *header = buf;
1281 char *end;
1282 unsigned long chunksize;
1283
1284 /* empty lines are used as trailer */
1285 if (*header == '\0')
1286 return 1;
1287
1288 /* strip CRLF and any optional chunk extension */
1289 header[strcspn(header, ";\r\n")] = '\0';
1290 errno(*__errno()) = 0;
1291 chunksize = strtoul(header, &end, 16);
1292 if (header[0] == '\0' || *end != '\0' || (errno(*__errno()) == ERANGE34 &&
1293 chunksize == ULONG_MAX(9223372036854775807L *2UL+1UL)) || chunksize > INT_MAX2147483647)
1294 return -1;
1295
1296 conn->iosz = chunksize;
1297 return 0;
1298}
1299
1300static enum res
1301http_read(struct http_connection *conn)
1302{
1303 ssize_t s;
1304 char *buf;
1305 int done;
1306
1307 if (conn->bufpos > 0)
1308 goto again;
1309
1310read_more:
1311 s = tls_read(conn->tls, conn->buf + conn->bufpos,
1312 conn->bufsz - conn->bufpos);
1313 if (s == -1) {
1314 warnx("%s: TLS read: %s", http_info(conn->host),
1315 tls_error(conn->tls));
1316 return http_failed(conn);
1317 } else if (s == TLS_WANT_POLLIN-2) {
1318 return WANT_POLLIN;
1319 } else if (s == TLS_WANT_POLLOUT-3) {
1320 return WANT_POLLOUT;
1321 }
1322
1323 if (s == 0) {
1324 if (conn->req)
1325 warnx("%s: short read, connection closed",
1326 http_info(conn->req->uri));
1327 return http_failed(conn);
1328 }
1329
1330 conn->bufpos += s;
1331
1332again:
1333 switch (conn->state) {
1334 case STATE_PROXY_STATUS:
1335 buf = http_get_line(conn);
1336 if (buf == NULL((void*)0))
1337 goto read_more;
1338 if (http_parse_status(conn, buf) == -1) {
1339 free(buf);
1340 return http_failed(conn);
1341 }
1342 free(buf);
1343 conn->state = STATE_PROXY_RESPONSE;
1344 goto again;
1345 case STATE_PROXY_RESPONSE:
1346 while (1) {
1347 buf = http_get_line(conn);
1348 if (buf == NULL((void*)0))
1349 goto read_more;
1350 /* empty line, end of header */
1351 if (*buf == '\0') {
1352 free(buf);
1353 break;
1354 }
1355 free(buf);
1356 }
1357 /* proxy is ready to take connection */
1358 if (conn->status == 200) {
1359 conn->state = STATE_CONNECT;
1360 return http_tls_connect(conn);
1361 }
1362 return http_failed(conn);
1363 case STATE_RESPONSE_STATUS:
1364 buf = http_get_line(conn);
1365 if (buf == NULL((void*)0))
1366 goto read_more;
1367
1368 if (http_parse_status(conn, buf) == -1) {
1369 free(buf);
1370 return http_failed(conn);
1371 }
1372 free(buf);
1373 conn->state = STATE_RESPONSE_HEADER;
1374 goto again;
1375 case STATE_RESPONSE_HEADER:
1376 done = 0;
1377 while (!done) {
1378 int rv;
1379
1380 buf = http_get_line(conn);
1381 if (buf == NULL((void*)0))
1382 goto read_more;
1383
1384 rv = http_parse_header(conn, buf);
1385 free(buf);
1386
1387 if (rv == -1)
1388 return http_failed(conn);
1389 if (rv == 0)
1390 done = 1;
1391 }
1392
1393 /* Check status header and decide what to do next */
1394 if (http_isok(conn) || http_isredirect(conn)) {
1395 if (http_isredirect(conn))
1396 http_redirect(conn);
1397
1398 conn->totalsz = 0;
1399 if (conn->chunked)
1400 conn->state = STATE_RESPONSE_CHUNKED_HEADER;
1401 else
1402 conn->state = STATE_RESPONSE_DATA;
1403 goto again;
1404 } else if (conn->status == 100 || conn->status == 103) {
1405 conn->state = STATE_RESPONSE_STATUS;
1406 } else if (conn->status == 304) {
1407 return http_done(conn, HTTP_NOT_MOD);
1408 }
1409
1410 return http_failed(conn);
1411 case STATE_RESPONSE_DATA:
1412 if (conn->bufpos != conn->bufsz &&
1413 conn->iosz > (off_t)conn->bufpos)
1414 goto read_more;
1415
1416 /* got a full buffer full of data */
1417 if (conn->req == NULL((void*)0)) {
1418 /*
1419 * After redirects all data needs to be discarded.
1420 */
1421 if (conn->iosz < (off_t)conn->bufpos) {
1422 conn->bufpos -= conn->iosz;
1423 conn->iosz = 0;
1424 } else {
1425 conn->iosz -= conn->bufpos;
1426 conn->bufpos = 0;
1427 }
1428 if (conn->chunked)
1429 conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1430 else
1431 conn->state = STATE_RESPONSE_DATA;
1432 goto read_more;
1433 }
1434
1435 conn->state = STATE_WRITE_DATA;
1436 return WANT_POLLOUT;
1437 case STATE_RESPONSE_CHUNKED_HEADER:
1438 assert(conn->iosz == 0)((conn->iosz == 0) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 1438, __func__, "conn->iosz == 0"))
;
1439
1440 buf = http_get_line(conn);
1441 if (buf == NULL((void*)0))
1442 goto read_more;
1443 if (http_parse_chunked(conn, buf) != 0) {
1444 warnx("%s: bad chunk encoding", http_info(conn->host));
1445 free(buf);
1446 return http_failed(conn);
1447 }
1448 free(buf);
1449
1450 /*
1451 * check if transfer is done, in which case the last trailer
1452 * still needs to be processed.
1453 */
1454 if (conn->iosz == 0) {
1455 conn->chunked = 0;
1456 conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1457 goto again;
1458 }
1459
1460 conn->state = STATE_RESPONSE_DATA;
1461 goto again;
1462 case STATE_RESPONSE_CHUNKED_TRAILER:
1463 buf = http_get_line(conn);
1464 if (buf == NULL((void*)0))
1465 goto read_more;
1466 if (http_parse_chunked(conn, buf) != 1) {
1467 warnx("%s: bad chunk encoding", http_info(conn->host));
1468 free(buf);
1469 return http_failed(conn);
1470 }
1471 free(buf);
1472
1473 /* if chunked got cleared then the transfer is over */
1474 if (conn->chunked == 0)
1475 return http_done(conn, HTTP_OK);
1476
1477 conn->state = STATE_RESPONSE_CHUNKED_HEADER;
1478 goto again;
1479 default:
1480 errx(1, "unexpected http state");
1481 }
1482}
1483
1484/*
1485 * Send out the HTTP request. When done, replace buffer with the read buffer.
1486 */
1487static enum res
1488http_write(struct http_connection *conn)
1489{
1490 ssize_t s;
1491
1492 assert(conn->state == STATE_REQUEST)((conn->state == STATE_REQUEST) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 1492, __func__, "conn->state == STATE_REQUEST"))
;
1493
1494 while (conn->bufpos < conn->bufsz) {
1495 s = tls_write(conn->tls, conn->buf + conn->bufpos,
1496 conn->bufsz - conn->bufpos);
1497 if (s == -1) {
1498 warnx("%s: TLS write: %s", http_info(conn->host),
1499 tls_error(conn->tls));
1500 return http_failed(conn);
1501 } else if (s == TLS_WANT_POLLIN-2) {
1502 return WANT_POLLIN;
1503 } else if (s == TLS_WANT_POLLOUT-3) {
1504 return WANT_POLLOUT;
1505 }
1506
1507 conn->bufpos += s;
1508 }
1509
1510 /* done writing, first thing we need the status */
1511 conn->state = STATE_RESPONSE_STATUS;
1512
1513 /* free write buffer and allocate the read buffer */
1514 free(conn->buf);
1515 conn->bufpos = 0;
1516 conn->bufsz = HTTP_BUF_SIZE(32 * 1024);
1517 if ((conn->buf = malloc(conn->bufsz)) == NULL((void*)0))
1518 err(1, NULL((void*)0));
1519
1520 return http_read(conn);
1521}
1522
1523static enum res
1524proxy_read(struct http_connection *conn)
1525{
1526 ssize_t s;
1527 char *buf;
1528 int done;
1529
1530 s = read(conn->fd, conn->buf + conn->bufpos,
1531 conn->bufsz - conn->bufpos);
1532 if (s == -1) {
1533 warn("%s: read", http_info(conn->host));
1534 return http_failed(conn);
1535 }
1536
1537 if (s == 0) {
1538 if (conn->req)
1539 warnx("%s: short read, connection closed",
1540 http_info(conn->host));
1541 return http_failed(conn);
1542 }
1543
1544 conn->bufpos += s;
1545
1546again:
1547 switch (conn->state) {
1548 case STATE_PROXY_STATUS:
1549 buf = http_get_line(conn);
1550 if (buf == NULL((void*)0))
1551 return WANT_POLLIN;
1552 if (http_parse_status(conn, buf) == -1) {
1553 free(buf);
1554 return http_failed(conn);
1555 }
1556 free(buf);
1557 conn->state = STATE_PROXY_RESPONSE;
1558 goto again;
1559 case STATE_PROXY_RESPONSE:
1560 done = 0;
1561 while (!done) {
1562 buf = http_get_line(conn);
1563 if (buf == NULL((void*)0))
1564 return WANT_POLLIN;
1565 /* empty line, end of header */
1566 if (*buf == '\0')
1567 done = 1;
1568 free(buf);
1569 }
1570 /* proxy is ready, connect to remote */
1571 if (conn->status == 200) {
1572 conn->state = STATE_CONNECT;
1573 return http_tls_connect(conn);
1574 }
1575 return http_failed(conn);
1576 default:
1577 errx(1, "unexpected http state");
1578 }
1579}
1580
1581/*
1582 * Send out the proxy request. When done, replace buffer with the read buffer.
1583 */
1584static enum res
1585proxy_write(struct http_connection *conn)
1586{
1587 ssize_t s;
1588
1589 assert(conn->state == STATE_PROXY_REQUEST)((conn->state == STATE_PROXY_REQUEST) ? (void)0 : __assert2
("/usr/src/usr.sbin/rpki-client/http.c", 1589, __func__, "conn->state == STATE_PROXY_REQUEST"
))
;
1590
1591 s = write(conn->fd, conn->buf + conn->bufpos,
1592 conn->bufsz - conn->bufpos);
1593 if (s == -1) {
1594 warn("%s: write", http_info(conn->host));
1595 return http_failed(conn);
1596 }
1597 conn->bufpos += s;
1598 if (conn->bufpos < conn->bufsz)
1599 return WANT_POLLOUT;
1600
1601 /* done writing, first thing we need the status */
1602 conn->state = STATE_PROXY_STATUS;
1603
1604 /* free write buffer and allocate the read buffer */
1605 free(conn->buf);
1606 conn->bufpos = 0;
1607 conn->bufsz = HTTP_BUF_SIZE(32 * 1024);
1608 if ((conn->buf = malloc(conn->bufsz)) == NULL((void*)0))
1609 err(1, NULL((void*)0));
1610
1611 return WANT_POLLIN;
1612}
1613
1614/*
1615 * Properly shutdown the TLS session else move connection into free state.
1616 */
1617static enum res
1618http_close(struct http_connection *conn)
1619{
1620 assert(conn->state == STATE_IDLE || conn->state == STATE_CLOSE)((conn->state == STATE_IDLE || conn->state == STATE_CLOSE
) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 1620, __func__, "conn->state == STATE_IDLE || conn->state == STATE_CLOSE"
))
;
1621
1622 conn->state = STATE_CLOSE;
1623
1624 if (conn->tls != NULL((void*)0)) {
1625 switch (tls_close(conn->tls)) {
1626 case TLS_WANT_POLLIN-2:
1627 return WANT_POLLIN;
1628 case TLS_WANT_POLLOUT-3:
1629 return WANT_POLLOUT;
1630 case 0:
1631 case -1:
1632 break;
1633 }
1634 }
1635
1636 conn->state = STATE_FREE;
1637 return DONE;
1638}
1639
1640/*
1641 * Write data into provided file descriptor. If all data got written
1642 * the connection may change into idle state.
1643 */
1644static enum res
1645data_write(struct http_connection *conn)
1646{
1647 ssize_t s;
1648 size_t bsz = conn->bufpos;
1649
1650 assert(conn->state == STATE_WRITE_DATA)((conn->state == STATE_WRITE_DATA) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 1650, __func__, "conn->state == STATE_WRITE_DATA"))
;
1651
1652 if (conn->iosz < (off_t)bsz)
1653 bsz = conn->iosz;
1654
1655 s = write(conn->req->outfd, conn->buf, bsz);
1656 if (s == -1) {
1657 warn("%s: data write", http_info(conn->req->uri));
1658 return http_failed(conn);
1659 }
1660
1661 conn->totalsz += s;
1662 if (conn->totalsz > MAX_CONTENTLEN(2 * 1024 * 1024 * 1024LL)) {
1663 warn("%s: too much data offered", http_info(conn->req->uri));
1664 return http_failed(conn);
1665 }
1666
1667 conn->bufpos -= s;
1668 conn->iosz -= s;
1669 memmove(conn->buf, conn->buf + s, conn->bufpos);
1670
1671 /* check if regular file transfer is finished */
1672 if (!conn->chunked && conn->iosz == 0)
1673 return http_done(conn, HTTP_OK);
1674
1675 /* all data written, switch back to read */
1676 if (conn->bufpos == 0 || conn->iosz == 0) {
1677 if (conn->chunked && conn->iosz == 0)
1678 conn->state = STATE_RESPONSE_CHUNKED_TRAILER;
1679 else
1680 conn->state = STATE_RESPONSE_DATA;
1681 return http_read(conn);
1682 }
1683
1684 /* still more data to write in buffer */
1685 return WANT_POLLOUT;
1686}
1687
1688/*
1689 * Do one IO call depending on the connection state.
1690 * Return WANT_POLLIN or WANT_POLLOUT to poll for more data.
1691 * If 0 is returned this stage is finished and the protocol should move
1692 * to the next stage by calling http_nextstep(). On error return -1.
1693 */
1694static enum res
1695http_handle(struct http_connection *conn)
1696{
1697 assert (conn->pfd != NULL && conn->pfd->revents != 0)((conn->pfd != ((void*)0) && conn->pfd->revents
!= 0) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/http.c"
, 1697, __func__, "conn->pfd != NULL && conn->pfd->revents != 0"
))
;
1698
1699 conn->io_time = 0;
1700
1701 switch (conn->state) {
1702 case STATE_CONNECT:
1703 return http_finish_connect(conn);
1704 case STATE_TLSCONNECT:
1705 return http_tls_handshake(conn);
1706 case STATE_REQUEST:
1707 return http_write(conn);
1708 case STATE_PROXY_REQUEST:
1709 return proxy_write(conn);
1710 case STATE_PROXY_STATUS:
1711 case STATE_PROXY_RESPONSE:
1712 return proxy_read(conn);
1713 case STATE_RESPONSE_STATUS:
1714 case STATE_RESPONSE_HEADER:
1715 case STATE_RESPONSE_DATA:
1716 case STATE_RESPONSE_CHUNKED_HEADER:
1717 case STATE_RESPONSE_CHUNKED_TRAILER:
1718 return http_read(conn);
1719 case STATE_WRITE_DATA:
1720 return data_write(conn);
1721 case STATE_CLOSE:
1722 return http_close(conn);
1723 case STATE_IDLE:
1724 conn->state = STATE_RESPONSE_HEADER;
1725 return http_read(conn);
1726 case STATE_FREE:
1727 errx(1, "bad http state");
1728 }
1729 errx(1, "unknown http state");
1730}
1731
1732/*
1733 * Initialisation done before pledge() call to load certificates.
1734 */
1735static void
1736http_setup(void)
1737{
1738 char *httpproxy;
1739
1740 tls_config = tls_config_new();
1741 if (tls_config == NULL((void*)0))
4
Assuming 'tls_config' is not equal to NULL
5
Taking false branch
1742 errx(1, "tls config failed");
1743
1744#if 0
1745 /* TODO Should we allow extra protos and ciphers? */
1746 if (tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL((1 << 1)|(1 << 2)| (1 << 3)|(1 << 4)
)
) == -1)
1747 errx(1, "tls set protocols failed: %s",
1748 tls_config_error(tls_config));
1749 if (tls_config_set_ciphers(tls_config, "legacy") == -1)
1750 errx(1, "tls set ciphers failed: %s",
1751 tls_config_error(tls_config));
1752#endif
1753
1754 /* load cert file from disk now */
1755 tls_ca_mem = tls_load_file(tls_default_ca_cert_file(),
1756 &tls_ca_size, NULL((void*)0));
1757 if (tls_ca_mem == NULL((void*)0))
6
Assuming 'tls_ca_mem' is not equal to NULL
7
Taking false branch
1758 err(1, "tls_load_file: %s", tls_default_ca_cert_file());
1759 tls_config_set_ca_mem(tls_config, tls_ca_mem, tls_ca_size);
1760
1761 if ((httpproxy = getenv("http_proxy")) != NULL((void*)0) && *httpproxy == '\0')
8
Assuming the condition is true
9
Assuming the condition is false
10
Taking false branch
1762 httpproxy = NULL((void*)0);
1763
1764 proxy_parse_uri(httpproxy);
11
Calling 'proxy_parse_uri'
1765}
1766
1767void
1768proc_http(char *bind_addr, int fd)
1769{
1770 struct pollfd pfds[NPFDS(64 + 1)];
1771 struct http_connection *conn, *nc;
1772 struct http_request *req, *nr;
1773 struct ibuf *b, *inbuf = NULL((void*)0);
1774
1775 if (bind_addr != NULL((void*)0)) {
1
Assuming 'bind_addr' is equal to NULL
2
Taking false branch
1776 struct addrinfo hints, *res;
1777
1778 bzero(&hints, sizeof(hints));
1779 hints.ai_family = AF_UNSPEC0;
1780 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
1781 hints.ai_flags = AI_NUMERICHOST4;
1782 if (getaddrinfo(bind_addr, NULL((void*)0), &hints, &res) == 0) {
1783 memcpy(&http_bindaddr, res->ai_addr, res->ai_addrlen);
1784 freeaddrinfo(res);
1785 }
1786 }
1787 http_setup();
3
Calling 'http_setup'
1788
1789 if (pledge("stdio inet dns recvfd", NULL((void*)0)) == -1)
1790 err(1, "pledge");
1791
1792 memset(&pfds, 0, sizeof(pfds));
1793
1794 msgbuf_init(&msgq);
1795 msgq.fd = fd;
1796
1797 for (;;) {
1798 time_t now;
1799 int timeout;
1800 size_t i;
1801
1802 pfds[0].fd = fd;
1803 pfds[0].events = POLLIN0x0001;
1804 if (msgq.queued)
1805 pfds[0].events |= POLLOUT0x0004;
1806
1807 i = 1;
1808 timeout = INFTIM(-1);
1809 now = getmonotime();
1810 LIST_FOREACH(conn, &active, entry)for((conn) = ((&active)->lh_first); (conn)!= ((void*)0
); (conn) = ((conn)->entry.le_next))
{
1811 if (conn->io_time == 0)
1812 conn->io_time = now + HTTP_IO_TIMEOUT(3 * 60);
1813
1814 if (conn->io_time <= now)
1815 timeout = 0;
1816 else {
1817 int diff = conn->io_time - now;
1818 diff *= 1000;
1819 if (timeout == INFTIM(-1) || diff < timeout)
1820 timeout = diff;
1821 }
1822 if (conn->state == STATE_WRITE_DATA)
1823 pfds[i].fd = conn->req->outfd;
1824 else
1825 pfds[i].fd = conn->fd;
1826
1827 pfds[i].events = conn->events;
1828 conn->pfd = &pfds[i];
1829 i++;
1830 if (i > NPFDS(64 + 1))
1831 errx(1, "too many connections");
1832 }
1833 LIST_FOREACH(conn, &idle, entry)for((conn) = ((&idle)->lh_first); (conn)!= ((void*)0);
(conn) = ((conn)->entry.le_next))
{
1834 if (conn->idle_time <= now)
1835 timeout = 0;
1836 else {
1837 int diff = conn->idle_time - now;
1838 diff *= 1000;
1839 if (timeout == INFTIM(-1) || diff < timeout)
1840 timeout = diff;
1841 }
1842 pfds[i].fd = conn->fd;
1843 pfds[i].events = POLLIN0x0001;
1844 conn->pfd = &pfds[i];
1845 i++;
1846 if (i > NPFDS(64 + 1))
1847 errx(1, "too many connections");
1848 }
1849
1850 if (poll(pfds, i, timeout) == -1)
1851 err(1, "poll");
1852
1853 if (pfds[0].revents & POLLHUP0x0010)
1854 break;
1855 if (pfds[0].revents & POLLOUT0x0004) {
1856 switch (msgbuf_write(&msgq)) {
1857 case 0:
1858 errx(1, "write: connection closed");
1859 case -1:
1860 err(1, "write");
1861 }
1862 }
1863 if (pfds[0].revents & POLLIN0x0001) {
1864 b = io_buf_recvfd(fd, &inbuf);
1865 if (b != NULL((void*)0)) {
1866 unsigned int id;
1867 char *uri;
1868 char *mod;
1869
1870 io_read_buf(b, &id, sizeof(id));
1871 io_read_str(b, &uri);
1872 io_read_str(b, &mod);
1873
1874 /* queue up new requests */
1875 http_req_new(id, uri, mod, 0, b->fd);
1876 ibuf_free(b);
1877 }
1878 }
1879
1880 now = getmonotime();
1881 /* process idle connections */
1882 LIST_FOREACH_SAFE(conn, &idle, entry, nc)for ((conn) = ((&idle)->lh_first); (conn) && (
(nc) = ((conn)->entry.le_next), 1); (conn) = (nc))
{
1883 if (conn->pfd != NULL((void*)0) && conn->pfd->revents != 0)
1884 http_do(conn, http_handle);
1885 else if (conn->idle_time <= now)
1886 http_do(conn, http_close);
1887
1888 if (conn->state == STATE_FREE)
1889 http_free(conn);
1890 }
1891
1892 /* then active http requests */
1893 LIST_FOREACH_SAFE(conn, &active, entry, nc)for ((conn) = ((&active)->lh_first); (conn) &&
((nc) = ((conn)->entry.le_next), 1); (conn) = (nc))
{
1894 /* check if event is ready */
1895 if (conn->pfd != NULL((void*)0) && conn->pfd->revents != 0)
1896 http_do(conn, http_handle);
1897 else if (conn->io_time <= now) {
1898 warnx("%s: timeout, connection closed",
1899 http_info(conn->host));
1900 http_do(conn, http_failed);
1901 }
1902
1903 if (conn->state == STATE_FREE)
1904 http_free(conn);
1905 }
1906
1907
1908 TAILQ_FOREACH_SAFE(req, &queue, entry, nr)for ((req) = ((&queue)->tqh_first); (req) != ((void*)0
) && ((nr) = ((req)->entry.tqe_next), 1); (req) = (
nr))
1909 if (!http_req_schedule(req))
1910 break;
1911 }
1912
1913 exit(0);
1914}