AmendHub

Download

jcs

/

detritus

/

request.c

 

(View History)

jcs   request: Add gopher port Latest amendment: 55 on 2024-12-12

1 /*
2 * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "detritus.h"
22
23 enum {
24 REQ_STATE_NEGOTIATING = 1,
25 REQ_STATE_SENDING_REQUEST,
26 REQ_STATE_PARSING_RESPONSE
27 };
28
29 struct default_port {
30 char *scheme;
31 unsigned short port;
32 } default_ports[] = {
33 { "finger", FINGER_PORT },
34 { "gemini", GEMINI_PORT },
35 { "gopher", GOPHER_PORT },
36 { "http", HTTP_PORT },
37 { "https", HTTPS_PORT },
38 };
39
40 /* TCP functions */
41 bool request_tcp_connect(struct request *request);
42 bool request_tcp_send(struct request *request, char *data, size_t len);
43 ssize_t request_tcp_avail(struct request *request);
44 ssize_t request_tcp_read(struct request *request, char *buf, size_t len);
45
46 bool request_tls_init(struct request *request);
47 void request_tls_cleanup(struct tls_request *request);
48
49 ssize_t request_tls_read_tls_ciphertext(struct request *request);
50 ssize_t request_tls_read_tcp_ciphertext(struct request *request,
51 short space);
52 ssize_t request_tls_send_plaintext(struct request *request,
53 size_t space, request_data_queuer queuer, void *cookie);
54 ssize_t request_tls_read_plaintext(struct request *request,
55 request_data_consumer consumer, void *cookie);
56
57 enum {
58 URI_STATE_SCHEME,
59 URI_STATE_HOSTNAME,
60 URI_STATE_PORT,
61 URI_STATE_PATH,
62 URI_STATE_QUERY
63 };
64
65 struct URI *
66 parse_uri(char *uristr)
67 {
68 register char c;
69 static char scheme[URI_MAX_SCHEME_LEN + 1];
70 static char hostname[URI_MAX_HOSTNAME_LEN + 1];
71 static char sport[URI_MAX_PORT_LEN + 1];
72 static char path[URI_MAX_PATH_LEN + 1];
73 static char query[URI_MAX_QUERY_LEN + 1];
74 static char str[URI_MAX_STR_LEN + 1];
75 struct URI *uri;
76 char *data;
77 size_t n, uri_len, scheme_len, hostname_len, sport_len, path_len,
78 query_len, str_len, cat_len, size;
79 short state, count, pos;
80 long lport;
81 unsigned short port;
82
83 /* TODO: handle host:port */
84 /* TODO: handle //user:pass@host */
85
86 uri_len = strlen(uristr);
87 scheme[0] = '\0';
88 hostname[0] = '\0';
89 port = 0;
90 sport[0] = '\0';
91 path[0] = '\0';
92 query[0] = '\0';
93 str[0] = '\0';
94 state = URI_STATE_SCHEME;
95 pos = 0;
96
97 for (n = 0; n < uri_len; n++) {
98 c = uristr[n];
99
100 switch (state) {
101 case URI_STATE_SCHEME:
102 if (pos == sizeof(scheme))
103 return NULL;
104 if (c == ':') {
105 if (uristr[n + 1] != '/' || uristr[n + 2] != '/')
106 /* TODO: support "mailto:" type URIs? */
107 return NULL;
108 scheme[pos] = '\0';
109 pos = 0;
110 n += 2;
111 state = URI_STATE_HOSTNAME;
112 break;
113 }
114 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
115 scheme[pos++] = c;
116 scheme[pos] = '\0';
117 } else
118 return NULL;
119 break;
120 case URI_STATE_HOSTNAME:
121 if (pos == sizeof(hostname))
122 return NULL;
123 if (c == '/' || c == '?') {
124 hostname[pos] = '\0';
125 pos = 0;
126 if (c == '?') {
127 state = URI_STATE_QUERY;
128 n++;
129 } else {
130 state = URI_STATE_PATH;
131 path[0] = '/';
132 path[1] = '\0';
133 pos = 1;
134 }
135 break;
136 }
137 if (c == ':') {
138 hostname[pos] = '\0';
139 pos = 0;
140 state = URI_STATE_PORT;
141 break;
142 }
143 if ((c >= '0' && c <= '9') || c == '.' ||
144 (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
145 ((c == '.' || c == '-') && pos != 0)) {
146 hostname[pos++] = c;
147 hostname[pos] = '\0';
148 } else
149 return NULL;
150 break;
151 case URI_STATE_PORT:
152 if (pos == sizeof(sport))
153 return NULL;
154 if (c == '/' || c == '?') {
155 sport[pos] = '\0';
156 pos = 0;
157 if (c == '?')
158 state = URI_STATE_QUERY;
159 else {
160 state = URI_STATE_PATH;
161 path[0] = '/';
162 path[1] = '\0';
163 pos = 1;
164 }
165 break;
166 }
167 if (c >= '0' && c <= '9') {
168 sport[pos++] = c;
169 sport[pos] = '\0';
170 } else
171 return NULL;
172 break;
173 case URI_STATE_PATH:
174 if (pos == sizeof(path))
175 return NULL;
176 if (c == '?') {
177 path[pos] = '\0';
178 pos = 0;
179 state = URI_STATE_QUERY;
180 break;
181 }
182 /* XXX: should we encode non-ascii here? */
183 path[pos++] = c;
184 path[pos] = '\0';
185 break;
186 case URI_STATE_QUERY:
187 if (pos == sizeof(path))
188 return NULL;
189 query[pos++] = c;
190 query[pos] = '\0';
191 break;
192 }
193 }
194
195 if (state == URI_STATE_SCHEME)
196 return NULL;
197
198 if (hostname[0] == '\0')
199 return NULL;
200
201 if (sport[0]) {
202 lport = atol(sport);
203 if (lport == 0 || lport > USHRT_MAX)
204 return NULL;
205 port = lport;
206 }
207
208 /* if the uri has a port but it's the default for the scheme, drop it */
209 for (n = 0; n < sizeof(default_ports) / sizeof(default_ports[0]); n++) {
210 if (strcasecmp(default_ports[n].scheme, scheme) == 0) {
211 if (port == default_ports[n].port) {
212 port = 0;
213 sport[0] = '\0';
214 }
215 break;
216 }
217 }
218
219 if (path[0] == '\0') {
220 path[0] = '/';
221 path[1] = '\0';
222 }
223
224 scheme_len = strlen(scheme);
225 hostname_len = strlen(hostname);
226 sport_len = strlen(sport);
227 path_len = strlen(path);
228 query_len = strlen(query);
229 str_len = scheme_len + 3 + hostname_len + path_len;
230 if (port)
231 str_len += 1 + sport_len;
232 if (query_len)
233 str_len += 1 + query_len;
234
235 size = sizeof(struct URI) + scheme_len + 1 + hostname_len + 1 +
236 path_len + 1 + query_len + 1 + str_len + 1;
237 uri = xmalloc(size);
238 if (uri == NULL)
239 return NULL;
240
241 uri->port = port;
242
243 data = (char *)uri + sizeof(struct URI);
244
245 uri->scheme = data;
246 memcpy(uri->scheme, scheme, scheme_len);
247 uri->scheme[scheme_len] = '\0';
248 data += scheme_len + 1;
249
250 uri->hostname = data;
251 memcpy(uri->hostname, hostname, hostname_len);
252 uri->hostname[hostname_len] = '\0';
253 data += hostname_len + 1;
254
255 uri->path = data;
256 memcpy(uri->path, path, path_len);
257 uri->path[path_len] = '\0';
258 data += path_len + 1;
259
260 uri->query = data;
261 memcpy(uri->query, query, query_len);
262 uri->query[query_len] = '\0';
263 data += query_len + 1;
264
265 uri->str = data;
266 cat_len = strlcpy(data, uri->scheme, str_len + 1);
267 cat_len = strlcat(data, "://", str_len + 1);
268 cat_len = strlcat(data, uri->hostname, str_len + 1);
269 if (port) {
270 cat_len = strlcat(data, ":", str_len + 1);
271 cat_len = strlcat(data, sport, str_len + 1);
272 }
273 cat_len = strlcat(data, path, str_len + 1);
274 if (query_len) {
275 cat_len = strlcat(data, "?", str_len + 1);
276 cat_len = strlcat(data, query, str_len + 1);
277 }
278
279 if (cat_len > str_len)
280 panic("URI overflow");
281
282 return uri;
283 }
284
285 struct URI *
286 build_relative_uri(struct URI *uri, char *relative, size_t len)
287 {
288 static char path[URI_MAX_PATH_LEN + 1];
289 static char str[URI_MAX_STR_LEN + 1];
290 size_t plen, n;
291 ssize_t slen;
292
293 /* http://a.b/c + //d/e -> http://d/e */
294 if (relative[0] == '/' && relative[1] == '/') {
295 /* retain scheme, new host, port, path, query */
296 slen = snprintf(str, sizeof(str), "%s:", uri->scheme);
297 if (len > sizeof(str) - slen - 1)
298 return NULL;
299 memcpy(str + slen, relative, len);
300 str[slen + len] = '\0';
301 return parse_uri(str);
302 }
303
304 /* if we can find :// before /, this looks like a whole new uri */
305 if (relative[0] != '/' && relative[0] != '?') {
306 for (n = 0; n <= len; n++) {
307 if (relative[n] == ':' && relative[n + 1] == '/' &&
308 relative[n + 2] == '/') {
309 /* scheme, not relative */
310 if (len > sizeof(str) - 1)
311 return NULL;
312 memcpy(str, relative, len);
313 str[len] = '\0';
314 return parse_uri(str);
315 }
316
317 if (relative[n] == '/')
318 break;
319 }
320 }
321
322 /* retain scheme, host, port, any following will set new path/query */
323 slen = snprintf(str, sizeof(str), "%s://%s", uri->scheme,
324 uri->hostname);
325 if (uri->port)
326 slen += snprintf(str + slen, sizeof(str) - slen, ":%d", uri->port);
327 if (len > sizeof(str) - slen - 1)
328 return NULL;
329
330 /* http://a.b/c + ?goose -> http://a.b/c?goose */
331 if (relative[0] == '?') {
332 slen = strlcat(str, uri->path, sizeof(str));
333 if (len + 1 > sizeof(str) - slen - 1)
334 return NULL;
335 memcpy(str + slen, relative, len);
336 str[slen + len] = '\0';
337 return parse_uri(str);
338 }
339
340 /* http://a.b/c + /d/e -> http://a.b/d/e */
341 if (relative[0] == '/') {
342 /* new path and query */
343 if (len > sizeof(str) - slen - 1)
344 return NULL;
345 memcpy(str + slen, relative, len);
346 str[slen + len] = '\0';
347 return parse_uri(str);
348 }
349
350 for (n = 0; n <= len; n++) {
351 /* http://a.b/c/d.html + e/f.html -> http://a.b/c/e/f.html */
352 if (relative[n] == '/' || n == len) {
353 /* remove last component in uri path up to slash */
354 plen = strlcpy(path, uri->path, sizeof(path));
355 while (plen && path[plen - 1] != '/') {
356 path[plen - 1] = '\0';
357 plen--;
358 }
359
360 if (plen == 0) {
361 path[0] = '/';
362 path[1] = '\0';
363 plen = 1;
364 }
365
366 if (plen >= sizeof(path) - 1)
367 plen = sizeof(path) - 2;
368
369 memcpy(path + plen, relative, len);
370 path[plen + len] = '\0';
371 snprintf(str, sizeof(str), "%s://%s%s", uri->scheme,
372 uri->hostname, path);
373 return parse_uri(str);
374 }
375 }
376
377 /* what the heck is this? */
378 Debugger();
379 return parse_uri(relative);
380 }
381
382 char *
383 uri_encode(unsigned char *str)
384 {
385 char *ret = NULL;
386 size_t len, n;
387 bool encode = false;
388 char a, b;
389
390 encode:
391 for (n = 0, len = 0; str[n] != '\0'; n++) {
392 if ((str[n] >= 'A' && str[n] <= 'Z') ||
393 (str[n] >= 'a' && str[n] <= 'z') ||
394 (str[n] >= '0' && str[n] <= '9') ||
395 (str[n] == '-' || str[n] == '_' || str[n] == '.' ||
396 str[n] == '~')) {
397 if (ret)
398 ret[len] = str[n];
399 len++;
400 } else {
401 if (ret) {
402 sprintf(ret + len, "%%%02X", str[n]);
403 }
404 len += 3;
405 }
406 }
407
408 if (ret) {
409 ret[len] = '\0';
410 return ret;
411 }
412
413 ret = xmalloc(len + 1);
414 if (ret == NULL) {
415 warn("Failed allocating %ld", len + 1);
416 return NULL;
417 }
418 len = 0;
419 goto encode;
420 }
421
422 struct request *
423 request_connect(struct browser *browser, char *hostname,
424 unsigned short port, bool tls, unsigned char tls_flags)
425 {
426 struct request *request;
427
428 request = xmalloczero(sizeof(struct request));
429 if (request == NULL)
430 return NULL;
431
432 request->browser = browser;
433 strlcpy(request->hostname, hostname, sizeof(request->hostname));
434 request->port = port;
435
436 if (!request_tcp_connect(request)) {
437 request_xfree(&request);
438 return NULL;
439 }
440
441 if (tls) {
442 request->tls_flags = tls_flags;
443 if (!request_tls_init(request)) {
444 request_xfree(&request);
445 return NULL;
446 }
447 }
448
449 return request;
450 }
451
452 void
453 request_xfree(struct request **requestp)
454 {
455 struct request *request = *requestp;
456
457 if (request->tls_id)
458 scsi_tls_close(request->tls_id);
459
460 if (request->stream) {
461 _TCPRelease(&request->iopb, request->stream, NULL, NULL, false);
462 request->stream = 0;
463 }
464
465 if (request->output)
466 xfree(&request->output);
467
468 xfree(requestp);
469 }
470
471 bool
472 request_data_shuffle(struct request *request, request_data_queuer queuer,
473 void *queuer_cookie, request_data_consumer consumer,
474 void *consumer_cookie)
475 {
476 size_t len;
477 ssize_t slen;
478 char *data;
479 short status, cipherspace, plainspace, tls_error;
480 EventRecord event;
481
482 do {
483 if (request->tls_id) {
484 status = scsi_tls_status(request->tls_id, &cipherspace,
485 &plainspace, &tls_error);
486
487 if ((status & 0x10) || (status & 0x1)) {
488 /* tls has plaintext data for us */
489 slen = request_tls_read_plaintext(request, consumer,
490 consumer_cookie);
491 if (slen < 0)
492 return false;
493 if (slen == 0)
494 status &= ~0x10;
495 }
496
497 if (status & 0x2) {
498 /* tls has ciphertext for tcp */
499 slen = request_tls_read_tls_ciphertext(request);
500 if (slen < 0)
501 return false;
502 if (slen == 0)
503 status &= ~0x2;
504 }
505
506 if (status & 0x8) {
507 /* tls can read plaintext from us */
508 slen = request_tls_send_plaintext(request, plainspace,
509 queuer, queuer_cookie);
510 if (slen < 0)
511 return false;
512 if (slen == 0)
513 status &= ~0x8;
514 }
515
516 if (status & 0x4) {
517 /* tls can read ciphertext from tcp */
518 slen = request_tls_read_tcp_ciphertext(request,
519 cipherspace);
520 if (slen < 0)
521 return false;
522 if (slen == 0)
523 status &= ~0x4;
524 }
525
526 /* only do this when we have no other statuses */
527 if (status == 0x1) {
528 /* closed */
529 if (tls_error != 0)
530 browser_statusf(request->browser,
531 "Error: TLS handshake failed: %d (TLS status "
532 "0x%x)", tls_error, status);
533 return false;
534 }
535 } else {
536 status = 0;
537
538 /* let the caller send out anything it has */
539 if (!queuer(request, queuer_cookie, &data, &len, false))
540 return false;
541
542 if (len > 0 && data != NULL) {
543 status = 1;
544 if (!request_tcp_send(request, data, len))
545 return false;
546
547 /* inform that we wrote len bytes */
548 if (!queuer(request, queuer_cookie, NULL, &len, true))
549 return false;
550 }
551
552 /* receive data and send it to the consumer */
553 slen = request_tcp_avail(request);
554 if (slen < 0) {
555 /* connection closed, let the consumer do one final read */
556 len = 0;
557 consumer(request, consumer_cookie, &data, &len, true);
558 return false;
559 }
560 if (slen > 0) {
561 status = 1;
562
563 /* get the consumer's buf to make sure it can handle slen */
564 data = NULL;
565 len = slen;
566 if (!consumer(request, consumer_cookie, &data, &len, false))
567 return false;
568
569 /* read into their buf */
570 slen = request_tcp_read(request, data, len);
571 if (slen < 0) {
572 /* read failed, give one final pass for processing */
573 request->tcp_done_reading = true;
574 len = 0;
575 consumer(request, consumer_cookie, &data, &len, true);
576 return false;
577 }
578 if (slen > 0) {
579 /* and let them know we read it */
580 len = slen;
581 if (!consumer(request, consumer_cookie, &data, &len,
582 true))
583 return false;
584 }
585 }
586 }
587
588 if (request->tcp_done_reading && request->input_len == 0)
589 return false;
590
591 if (CommandPeriodPressed()) {
592 browser_statusf(request->browser, "Request canceled");
593 return false;
594 }
595
596 /* if the user did anything else, let main event loop handle it */
597 if (GetNextEvent(everyEvent & ~updateMask, &event))
598 break;
599 } while (status != 0);
600
601 return true;
602 }
603
604 /* internal */
605
606 bool
607 request_tcp_connect(struct request *request)
608 {
609 size_t len;
610 short err;
611 ip_addr local_ip;
612 tcp_port local_port;
613
614 browser_statusf(request->browser,
615 "Resolving \"%s\"...", request->hostname);
616
617 err = DNSResolveName(request->hostname, &request->ip, NULL);
618 if (err) {
619 browser_statusf(request->browser,
620 "Error: Failed resolving %s: %d", request->hostname, err);
621 return false;
622 }
623
624 long2ip(request->ip, (char *)&request->ip_s);
625 browser_statusf(request->browser,
626 "Connecting to %s port %d...", request->ip_s, request->port);
627
628 err = _TCPCreate(&request->iopb, &request->stream, (Ptr)request->buf,
629 sizeof(request->buf), NULL, NULL, NULL, false);
630 if (err) {
631 browser_statusf(request->browser,
632 "Error: TCPCreate failed: %d", err);
633 return false;
634 }
635
636 err = _TCPActiveOpen(&request->iopb, request->stream, request->ip,
637 request->port, &local_ip, &local_port, NULL, NULL, false);
638 if (err) {
639 browser_statusf(request->browser,
640 "Error: Failed connecting to %s (%s) port %d: %d",
641 request->hostname, request->ip_s, request->port, err);
642 return false;
643 }
644
645 err = _TCPStatus(&request->iopb, request->stream, &request->status_pb,
646 NULL, NULL, false);
647 if (err) {
648 browser_statusf(request->browser,
649 "Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
650 request->hostname, request->ip_s, request->port, err);
651 return false;
652 }
653
654 return true;
655 }
656
657 ssize_t
658 request_tcp_avail(struct request *request)
659 {
660 short err;
661
662 if (request->iopb.ioResult > 0) {
663 BROWSER_DEBUGF((request->browser,
664 "TCP I/O Result %d, disconnecting", request->iopb.ioResult));
665 return -1;
666 }
667
668 err = _TCPStatus(&request->iopb, request->stream, &request->status_pb,
669 NULL, NULL, false);
670 if (err) {
671 browser_statusf(request->browser,
672 "Error: Bad TCPStatus: %d", err);
673 return -1;
674 }
675
676 if (request->status_pb.connectionState != ConnectionStateEstablished) {
677 BROWSER_DEBUGF((request->browser,
678 "TCP connection closed (state %d)",
679 request->status_pb.connectionState));
680 return -1;
681 }
682
683 return request->status_pb.amtUnreadData;
684 }
685
686 bool
687 request_tcp_send(struct request *request, char *data, size_t len)
688 {
689 short err;
690
691 memset(&request->wds, 0, sizeof(request->wds));
692 request->wds[0].ptr = (Ptr)data;
693 request->wds[0].length = len;
694
695 err = _TCPSend(&request->iopb, request->stream, request->wds, NULL,
696 NULL, false);
697 if (err) {
698 browser_statusf(request->browser,
699 "Error: TCPSend failed: %d", err);
700 return false;
701 }
702
703 return true;
704 }
705
706 ssize_t
707 request_tcp_read(struct request *request, char *buf, size_t len)
708 {
709 short err;
710 unsigned short slen;
711
712 slen = len;
713 err = _TCPRcv(&request->iopb, request->stream, (Ptr)buf, &slen, NULL,
714 NULL, false);
715 if (err) {
716 browser_statusf(request->browser,
717 "Error: Failed TCPRcv: %d", err);
718 return -1;
719 }
720
721 return slen;
722 }
723
724 /*
725 * TLS over TCP using BlueSCSI TLS Accelerator
726 */
727
728 bool
729 request_tls_init(struct request *request)
730 {
731 struct tls_init_request tls_req;
732 unsigned long time;
733
734 if (!scsi_can_do_tls()) {
735 warn("No TLS accelerator found, cannot make TLS requests");
736 return false;
737 }
738
739 browser_statusf(request->browser,
740 "Connected to %s, negotiating TLS...", request->hostname);
741
742 memset(&tls_req, 0, sizeof(tls_req));
743 strlcpy(tls_req.hostname, request->hostname, sizeof(tls_req.hostname));
744
745 time = MAC_TO_UNIX_TIME(Time);
746 tls_req.unix_time[0] = (time >> 24) & 0xff;
747 tls_req.unix_time[1] = (time >> 16) & 0xff;
748 tls_req.unix_time[2] = (time >> 8) & 0xff;
749 tls_req.unix_time[3] = (time) & 0xff;
750
751 tls_req.flags[1] = request->tls_flags;
752
753 request->tls_state = REQ_STATE_NEGOTIATING;
754 request->tls_id = scsi_tls_init(&tls_req);
755
756 if (request->tls_id == 0) {
757 browser_statusf(request->browser, "Error: TLS handshake failed!");
758 return false;
759 }
760
761 return true;
762 }
763
764 ssize_t
765 request_tls_read_tls_ciphertext(struct request *request)
766 {
767 size_t len;
768 unsigned char *buf;
769
770 /* read ciphertext from TLS and send it out TCP */
771
772 if (request->tcp_done_reading)
773 return 0;
774
775 if (request_tcp_avail(request) < 0) {
776 request->tcp_done_reading = true;
777 return 0;
778 }
779
780 /* this will point buf to scsi's static buffer */
781 buf = NULL;
782 len = scsi_tls_read(request->tls_id, &buf, 0, true);
783 if (len == 0)
784 return 0;
785 if (buf == NULL)
786 return -1;
787
788 BROWSER_DEBUGF((request->browser,
789 "Read %lu bytes of TLS ciphertext, forwarding to TCP", len));
790
791 /* result ignored? */
792 request_tcp_send(request, (char *)buf, len);
793
794 return len;
795 }
796
797 ssize_t
798 request_tls_read_tcp_ciphertext(struct request *request, short space)
799 {
800 size_t len, n;
801 ssize_t slen;
802 short err;
803
804 /* read ciphertext from TCP and send it to TLS */
805
806 if (request->input_len == sizeof(request->input) ||
807 request->tcp_done_reading)
808 goto forward_ciphertext;
809
810 slen = request_tcp_avail(request);
811 if (slen < 0)
812 request->tcp_done_reading = true;
813 if (slen <= 0)
814 goto forward_ciphertext;
815
816 slen = MIN(slen, sizeof(request->input) - request->input_len);
817 if (!slen) {
818 browser_statusf(request->browser,
819 "Error: No buffer space available in TCP input?");
820 goto forward_ciphertext;
821 }
822
823 slen = request_tcp_read(request,
824 (char *)(request->input + request->input_len), slen);
825 if (slen < 0)
826 goto forward_ciphertext;
827
828 request->input_len += slen;
829 BROWSER_DEBUGF((request->browser,
830 "Read %ld bytes of TCP ciphertext, forwarding to TLS", slen));
831
832 forward_ciphertext:
833 if (!request->input_len || !space)
834 return 0;
835
836 slen = MIN(request->input_len, space);
837 BROWSER_DEBUGF((request->browser,
838 "Forwarding %ld bytes of TCP ciphertext to TLS", slen));
839
840 len = scsi_tls_write(request->tls_id, request->input, slen, true);
841 if (len == 0) {
842 browser_statusf(request->browser,
843 "Error: Failed forwarding %ld bytes of ciphertext to TLS", slen);
844 return -1;
845 }
846
847 if (len == request->input_len)
848 request->input_len = 0;
849 else {
850 memmove(request->input, request->input + len,
851 request->input_len - len);
852 request->input_len -= len;
853 }
854 BROWSER_DEBUGF((request->browser,
855 "Wrote %ld bytes of TCP ciphertext to TLS, %ld left", len,
856 request->input_len));
857 return len;
858 }
859
860 ssize_t
861 request_tls_send_plaintext(struct request *request, size_t space,
862 request_data_queuer queuer, void *cookie)
863 {
864 size_t olen;
865 char *data;
866 size_t len;
867
868 /* send any plaintext from us to TLS */
869
870 /* queuer will set data and len to message content */
871 len = space;
872 if (!queuer(request, cookie, &data, &len, false))
873 return -1;
874 if (len == 0 || data == NULL)
875 return 0;
876
877 if (request->tls_state == REQ_STATE_NEGOTIATING)
878 request->tls_state = REQ_STATE_SENDING_REQUEST;
879 else if (request->tls_state != REQ_STATE_SENDING_REQUEST) {
880 browser_statusf(request->browser,
881 "Error: bogus state (%d) instead of SENDING_REQUEST, "
882 "disconnecting", request->tls_state);
883 return -1;
884 }
885
886 olen = MIN(len, space);
887 len = scsi_tls_write(request->tls_id, (unsigned char *)data, olen,
888 false);
889 if (!len) {
890 browser_statusf(request->browser,
891 "Error: Failed sending %ld bytes of plaintext to TLS", olen);
892 return -1;
893 }
894
895 /* inform that we wrote len bytes */
896 if (!queuer(request, cookie, NULL, &len, true))
897 return -1;
898
899 BROWSER_DEBUGF((request->browser,
900 "Wrote %ld bytes of plaintext to TLS", len));
901 return len;
902 }
903
904 ssize_t
905 request_tls_read_plaintext(struct request *request,
906 request_data_consumer consumer, void *cookie)
907 {
908 size_t len;
909 char *buf;
910
911 /* read as much plaintext from TLS as we can buffer */
912
913 len = 1024;
914 if (!consumer(request, cookie, &buf, &len, false))
915 return -1;
916
917 len = scsi_tls_read(request->tls_id, (unsigned char **)&buf, len, false);
918 if (len == 0)
919 return 0;
920
921 if (!consumer(request, cookie, &buf, &len, true))
922 return -1;
923
924 return len;
925 }