jcs
/detritus
/amendments
/26
*: Abstract TCP and TLS ops to request.c
jcs made amendment 26 about 1 year ago
--- browser.c Wed Nov 6 12:52:48 2024
+++ browser.c Wed Nov 6 22:33:34 2024
@@ -19,9 +19,7 @@
#include <string.h>
#include "detritus.h"
-#include "browser.h"
#include "focusable.h"
-#include "util.h"
#define PADDING 6
#define BROWSER_FONT geneva
@@ -235,9 +233,8 @@ browser_init(void)
focusable->atexit = browser_atexit;
focusable_add(focusable);
- snprintf(browser->status_text, sizeof(browser->status_text),
- "Hello, cyberpals!");
browser_update(focusable, NULL);
+ browser_statusf(browser, "Hello, cyberpals!");
return browser;
}
@@ -690,10 +687,7 @@ browser_go(struct browser *browser, short dir)
browser->loading_page = opage;
browser_update_buttons(browser);
- if ((*opage)->handler->init(*opage)) {
- HUnlock(opage);
- browser_commit_to_loading_page(browser);
- } else {
+ if (!(*opage)->handler->init(*opage)) {
(*opage)->handler->cleanup(*opage);
HUnlock(opage);
browser->loading_page = NULL;
--- browser.h Tue Nov 5 21:28:05 2024
+++ browser.h Wed Nov 6 22:31:48 2024
@@ -18,9 +18,9 @@
#define __BROWSER_H__
#include <stdlib.h>
+
#include "detritus.h"
#include "textview.h"
-#include "tcp.h"
#include "util.h"
//#define BROWSER_DEBUGF(x) browser_statusf
@@ -45,6 +45,28 @@ struct browser_link {
struct browser_link *next_link;
};
+typedef struct page **page_handle;
+
+struct page {
+ struct URI *uri;
+ struct browser *browser;
+ page_handle back_page;
+ page_handle fwd_page;
+ size_t content_size;
+#define PAGE_CONTENT_CHUNK_SIZE 1024
+ size_t content_len;
+ size_t content_pos;
+ struct uri_handler *handler;
+ void *fetch_cookie;
+ char type[32];
+ short state;
+ short parse_state;
+ struct URI *redir_to;
+ short download_frefnum;
+ size_t download_len;
+ char content[];
+};
+
struct browser {
WindowPtr win;
RgnHandle header;
@@ -66,6 +88,23 @@ struct browser {
unsigned long style;
struct browser_link *first_link;
struct browser_link *last_link;
+};
+
+struct uri_handler {
+ /* return a URI if this handler can process this string */
+ struct URI * (*parse_uri)(char *uristr);
+
+ /* do any post-init on a new page, like doing a request if no content */
+ bool (*init)(struct page *page);
+
+ /* continue fetching content */
+ bool (*fetch)(struct page *page);
+
+ /* parse existing content */
+ bool (*process)(struct page *page);
+
+ /* cleanup when done loading, such as freeing request */
+ void (*cleanup)(struct page *page);
};
struct browser * browser_init(void);
--- detritus.h Wed Nov 6 09:45:19 2024
+++ detritus.h Wed Nov 6 22:25:22 2024
@@ -17,6 +17,8 @@
#ifndef __DETRITUS_H__
#define __DETRITUS_H__
+#include "browser.h"
+#include "request.h"
#include "util.h"
#define PROGRAM_NAME "Detritus"
@@ -39,82 +41,8 @@
#define BOOKMARKS_MENU_ID 132
-extern uint8_t tls_req_last_id;
-
-struct tls_init_request {
- uint8_t flags[2];
-#define BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY (1 << 0)
- uint8_t unix_time[4];
- char hostname[256];
-};
-
-struct URI {
-#define URI_MAX_PROTOCOL_LEN 20
- char *protocol;
-#define URI_MAX_HOSTNAME_LEN 255
- char *hostname;
-#define URI_MAX_PATH_LEN 512
- char *path;
-#define URI_MAX_STR_LEN (URI_MAX_PROTOCOL_LEN + 3 + URI_MAX_HOSTNAME_LEN + \
- URI_MAX_PATH_LEN)
- char *str;
- unsigned short port;
-};
-
-typedef struct page **page_handle;
-struct page {
- struct URI *uri;
- struct browser *browser;
- page_handle back_page;
- page_handle fwd_page;
- size_t content_size;
-#define PAGE_CONTENT_CHUNK_SIZE 1024
- size_t content_len;
- size_t content_pos;
- struct uri_handler *handler;
- void *fetch_cookie;
- char type[32];
- short state;
- short parse_state;
- struct URI *redir_to;
- short download_frefnum;
- size_t download_len;
- char content[];
-};
-
-struct uri_handler {
- /* return a URI if this handler can process this string */
- struct URI * (*parse_uri)(char *uristr);
-
- /* do any post-init on a new page, like doing a request if no content */
- bool (*init)(struct page *page);
-
- /* continue fetching content */
- bool (*fetch)(struct page *page);
-
- /* parse existing content */
- bool (*process)(struct page *page);
-
- /* cleanup when done loading, such as freeing request */
- void (*cleanup)(struct page *page);
-};
-
extern MenuHandle file_menu, edit_menu, bookmarks_menu;
void menu_defaults(void);
-struct URI * parse_uri(char *uristr, char *restrict_protocol);
-struct URI * build_relative_uri(struct URI *uri, char *relative, size_t len);
-
-short scsi_find_tls(void);
-uint8_t scsi_tls_init(struct tls_init_request *req);
-bool scsi_tls_close(uint8_t tls_id);
-short scsi_tls_status(uint8_t tls_id, short *cipherspace,
- short *plainspace, short *error);
-size_t scsi_tls_read(uint8_t tls_id, unsigned char **buf, size_t max_size,
- bool cipher);
-size_t scsi_tls_write(uint8_t tls_id, unsigned char *buf, size_t buf_size,
- bool cipher);
-void scsi_cleanup(void);
-bool scsi_can_do_tls(void);
#endif
\ No newline at end of file
--- finger.c Wed Nov 6 12:57:34 2024
+++ finger.c Wed Nov 6 22:07:25 2024
@@ -19,18 +19,12 @@
#include <string.h>
#include "detritus.h"
-#include "browser.h"
#define FINGER_PORT 79
struct finger_request {
- unsigned char tcp_buf[(4 * 1500) + 2048]; /* 4*MTU + tcp_input */
+ struct tcp_request tcp;
- TCPiopb tcp_iopb;
- StreamPtr tcp_stream;
- wdsEntry tcp_wds[2];
- TCPStatusPB tcp_status_pb;
-
char query[128];
size_t query_len;
};
@@ -58,21 +52,17 @@ finger_parse_uri(char *uristr)
bool
finger_init_request(struct page *page)
{
- struct finger_request *req = NULL;
- size_t len;
- char ip_s[12 + 3 + 1];
- short err;
- ip_addr ip, local_ip;
- tcp_port port, local_port;
+ struct finger_request *finger = NULL;
if (page->content_len) {
/* already fetched, nothing to do here but reset */
page->content_pos = 0;
+ browser_commit_to_loading_page(page->browser);
return true;
}
- req = xmalloczero(sizeof(struct finger_request));
- if (req == NULL) {
+ finger = xmalloczero(sizeof(struct finger_request));
+ if (finger == NULL) {
warn("Out of memory");
return false;
}
@@ -80,137 +70,78 @@ finger_init_request(struct page *page)
if (page->uri->port == 0)
page->uri->port = FINGER_PORT;
- browser_statusf(page->browser, "Resolving \"%s\"...",
- page->uri->hostname);
-
- err = DNSResolveName(page->uri->hostname, &ip, NULL);
- if (err) {
- browser_statusf(page->browser, "Error: Failed resolving: %d", err);
+ if (!request_tcp_connect(&finger->tcp, page->browser,
+ page->uri->hostname, page->uri->port)) {
+ xfree(&finger);
return false;
}
- long2ip(ip, (char *)&ip_s);
- browser_statusf(page->browser, "Connecting to %s port %d...", ip_s,
- page->uri->port);
-
- err = _TCPCreate(&req->tcp_iopb, &req->tcp_stream, (Ptr)req->tcp_buf,
- sizeof(req->tcp_buf), NULL, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPCreate failed: %d", err);
- return false;
- }
+ browser_statusf(page->browser, "Connected to %s, sending request...",
+ page->uri->hostname);
- err = _TCPActiveOpen(&req->tcp_iopb, req->tcp_stream, ip,
- page->uri->port, &local_ip, &local_port, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed connecting to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- return false;
- }
-
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- return false;
- }
-
if (page->uri->path[0] == '\0' ||
(page->uri->path[0] == '/' && page->uri->path[1] == '\0'))
- req->query_len = snprintf(req->query, sizeof(req->query),
- "\r\n", page->uri->hostname);
+ finger->query_len = snprintf(finger->query, sizeof(finger->query),
+ "\r\n");
else
- req->query_len = snprintf(req->query, sizeof(req->query),
+ finger->query_len = snprintf(finger->query, sizeof(finger->query),
"%s\r\n", page->uri->path + 1);
- browser_statusf(page->browser, "Connected to %s, sending request...",
- page->uri->hostname);
-
- memset(&req->tcp_wds, 0, sizeof(req->tcp_wds));
- req->tcp_wds[0].ptr = (Ptr)req->query;
- req->tcp_wds[0].length = req->query_len;
-
- err = _TCPSend(&req->tcp_iopb, req->tcp_stream, req->tcp_wds, NULL,
- NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPSend failed: %d", err);
+ if (!request_tcp_send(&finger->tcp, page->browser, finger->query,
+ finger->query_len)) {
+ request_tcp_cleanup(&finger->tcp);
+ xfree(&finger);
return false;
}
+
+ page->fetch_cookie = finger;
+ browser_commit_to_loading_page(page->browser);
- page->fetch_cookie = req;
return true;
}
void
finger_cleanup(struct page *page)
{
- struct finger_request *req;
+ struct finger_request *finger;
- if (page->fetch_cookie == NULL)
+ finger = (struct finger_request *)(page->fetch_cookie);
+ if (finger == NULL)
return;
-
- req = (struct finger_request *)(page->fetch_cookie);
- if (req->tcp_stream)
- _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false);
-
+ request_tcp_cleanup(&finger->tcp);
xfree(&page->fetch_cookie);
}
bool
finger_fetch(struct page *page)
{
- struct finger_request *req;
- short err;
- unsigned short slen;
+ struct finger_request *finger;
+ ssize_t len;
- if (page->fetch_cookie == NULL)
+ finger = (struct finger_request *)(page->fetch_cookie);
+ if (finger == NULL)
return false;
- req = (struct finger_request *)(page->fetch_cookie);
-
- if (req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) {
- BROWSER_DEBUGF((page->browser, "TCP I/O Result %d, disconnecting",
- req->tcp_iopb.ioResult));
+ len = request_tcp_avail(&finger->tcp, page->browser);
+ if (len < -1)
return false;
- }
-
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: Bad TCPStatus: %d", err);
- return false;
- }
-
- if (req->tcp_status_pb.connectionState !=
- ConnectionStateEstablished) {
- BROWSER_DEBUGF((page->browser, "TCP connection closed (state %d)",
- req->tcp_status_pb.connectionState));
- return false;
- }
-
- slen = req->tcp_status_pb.amtUnreadData;
- if (slen == 0)
+ if (len == 0)
return true;
- if (page->content_len + slen >= page->content_size) {
- page = browser_grow_page_content(page, slen);
+ if (page->content_len + len >= page->content_size) {
+ page = browser_grow_page_content(page, len);
if (page == NULL)
return false;
- req = (struct finger_request *)(page->fetch_cookie);
+ finger = (struct finger_request *)(page->fetch_cookie);
}
- err = _TCPRcv(&req->tcp_iopb, req->tcp_stream,
- (Ptr)(page->content + page->content_len), &slen, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: Failed TCPRcv: %d", err);
+ len = request_tcp_read(&finger->tcp, page->browser,
+ page->content + page->content_len, len);
+ if (len < 0)
return false;
- }
- page->content_len += slen;
+ page->content_len += len;
browser_statusf(page->browser, "Read %ld bytes", page->content_len);
return true;
--- gemini.c Wed Nov 6 13:06:39 2024
+++ gemini.c Wed Nov 6 22:07:32 2024
@@ -19,37 +19,20 @@
#include <string.h>
#include "detritus.h"
-#include "browser.h"
#define GEMINI_PORT 1965
enum {
- REQ_STATE_NEGOTIATING = 1,
- REQ_STATE_SENDING_REQUEST,
- REQ_STATE_PARSING_RESPONSE
-};
-
-enum {
PARSE_STATE_HEADER,
PARSE_STATE_GEMTEXT,
PARSE_STATE_DOWNLOAD
};
struct gemini_request {
- uint8_t tls_id;
- unsigned char tcp_buf[(4 * 1500) + 2048]; /* 4*MTU + tcp_input */
- unsigned char tcp_input[2048];
- unsigned long tcp_input_len;
+ struct tls_request tls;
- TCPiopb tcp_iopb;
- StreamPtr tcp_stream;
- wdsEntry tcp_wds[2];
- TCPStatusPB tcp_status_pb;
- bool tcp_done_reading;
-
char message[URI_MAX_STR_LEN + 1];
size_t message_len;
-
short state;
};
@@ -59,10 +42,6 @@ bool gemini_fetch(struct page *page);
bool gemini_process(struct page *page);
void gemini_cleanup(struct page *page);
-static bool shuffle_read_tls_ciphertext(struct page *page);
-static bool shuffle_read_tcp_ciphertext(struct page *page, short space);
-static bool shuffle_tls_send_plaintext(struct page *page, short space);
-static bool shuffle_tls_read_plaintext(struct page *page);
static bool parse_header(struct page *page, char *str, size_t len);
struct uri_handler gemini_handler = {
@@ -82,13 +61,7 @@ gemini_parse_uri(char *uristr)
bool
gemini_init_request(struct page *page)
{
- struct gemini_request *req = NULL;
- struct tls_init_request tls_req;
- char ip_s[12 + 3 + 1];
- unsigned long time;
- short err;
- ip_addr ip, local_ip;
- tcp_port port, local_port;
+ struct gemini_request *gemini = NULL;
if (page->content_len) {
/* already fetched, nothing to do here but reset */
@@ -97,13 +70,8 @@ gemini_init_request(struct page *page)
return true;
}
- if (!scsi_can_do_tls()) {
- warn("No TLS accelerator found, cannot make Gemini requests");
- return false;
- }
-
- req = xmalloczero(sizeof(struct gemini_request));
- if (req == NULL) {
+ gemini = xmalloczero(sizeof(struct gemini_request));
+ if (gemini == NULL) {
warn("Out of memory");
return false;
}
@@ -111,376 +79,132 @@ gemini_init_request(struct page *page)
if (page->uri->port == 0)
page->uri->port = GEMINI_PORT;
- browser_statusf(page->browser, "Resolving \"%s\"...",
- page->uri->hostname);
-
- err = DNSResolveName(page->uri->hostname, &ip, NULL);
- if (err) {
- browser_statusf(page->browser, "Error: Failed resolving: %d", err);
- goto fail;
+ if (!request_tls_connect(&gemini->tls, page->browser,
+ page->uri->hostname, page->uri->port,
+ BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY)) {
+ xfree(&gemini);
+ return false;
}
-
- long2ip(ip, (char *)&ip_s);
- browser_statusf(page->browser, "Connecting to %s port %d...", ip_s,
- page->uri->port);
-
- err = _TCPCreate(&req->tcp_iopb, &req->tcp_stream, (Ptr)req->tcp_buf,
- sizeof(req->tcp_buf), NULL, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPCreate failed: %d", err);
- goto fail;
- }
- err = _TCPActiveOpen(&req->tcp_iopb, req->tcp_stream, ip,
- page->uri->port, &local_ip, &local_port, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed connecting to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- goto fail;
- }
+ gemini->message_len = snprintf(gemini->message,
+ sizeof(gemini->message), "%s\r\n", page->uri->str);
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- goto fail;
- }
-
- req->message_len = snprintf(req->message, sizeof(req->message),
- "%s\r\n", page->uri->str);
-
- browser_statusf(page->browser,
- "Connected to %s, performing TLS negotiation...",
- page->uri->hostname);
-
- memset(&tls_req, 0, sizeof(tls_req));
- strlcpy(tls_req.hostname, page->uri->hostname,
- sizeof(tls_req.hostname));
- time = MAC_TO_UNIX_TIME(Time);
- tls_req.unix_time[0] = (time >> 24) & 0xff;
- tls_req.unix_time[1] = (time >> 16) & 0xff;
- tls_req.unix_time[2] = (time >> 8) & 0xff;
- tls_req.unix_time[3] = (time) & 0xff;
- /* sad */
- tls_req.flags[1] = BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY;
+ page->fetch_cookie = gemini;
- req->tls_id = scsi_tls_init(&tls_req);
- req->state = REQ_STATE_NEGOTIATING;
-
- if (req->tls_id == 0) {
- browser_statusf(page->browser, "TLS handshake failed!");
- goto fail;
- }
-
- page->fetch_cookie = req;
return true;
-
-fail:
- if (req) {
- if (req->tcp_stream)
- _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false);
-
- xfree(&req);
- page->fetch_cookie = NULL;
- }
-
- return false;
}
void
gemini_cleanup(struct page *page)
{
- struct gemini_request *req;
+ struct gemini_request *gemini;
- if (page->fetch_cookie == NULL)
+ gemini = (struct gemini_request *)(page->fetch_cookie);
+ if (gemini == NULL)
return;
- req = (struct gemini_request *)(page->fetch_cookie);
-
- if (req->tcp_stream)
- _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false);
-
- if (req->tls_id)
- scsi_tls_close(req->tls_id);
-
+ request_tls_cleanup(&gemini->tls);
xfree(&page->fetch_cookie);
}
-bool
-gemini_fetch(struct page *page)
+static bool
+gemini_fetch_tls_write_callback(struct tls_request *request,
+ struct browser *browser, void *cookie, bool wrote, char **buf,
+ size_t *len)
{
+ struct gemini_request *gemini;
page_handle pageh;
- struct gemini_request *req;
- size_t len;
- unsigned short slen;
- short err, status;
- short cipherspace, plainspace, error;
- bool ret;
+ struct page *page;
+ size_t tlen;
- if (page->fetch_cookie == NULL)
+ if (cookie == NULL)
return false;
+
+ pageh = (page_handle)cookie;
+ HLock(pageh);
+ page = *pageh;
+ gemini = (struct gemini_request *)page->fetch_cookie;
- req = (struct gemini_request *)(page->fetch_cookie);
-
- if (req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) {
- BROWSER_DEBUGF((page->browser, "TCP I/O Result %d, disconnecting",
- req->tcp_iopb.ioResult));
+ if (page == NULL) {
+ HUnlock(pageh);
return false;
}
-
- status = scsi_tls_status(req->tls_id, &cipherspace, &plainspace,
- &error);
- pageh = (page_handle)RecoverHandle(page);
-
- while (status != 0) {
- if ((status & 0x1) && page->content_pos == page->content_len) {
- /* closed */
- if (error != 0)
- browser_statusf(page->browser,
- "Error: TLS handshake failed: %d (TLS status 0x%x)",
- error, status);
- return false;
+ if (wrote) {
+ if (*len == gemini->message_len) {
+ browser_statusf(browser, "Sent request, waiting for reply...");
+ gemini->message_len = 0;
+ } else {
+ memmove(gemini->message, gemini->message + *len,
+ gemini->message_len - *len);
+ gemini->message_len -= *len;
}
-
- if (status & 0x2) {
- /* tls has ciphertext for tcp */
- if (!shuffle_read_tls_ciphertext(page))
- return false;
- status &= ~0x2;
- }
-
- if ((status & 0x10) || page->content_pos < page->content_len) {
- /* tls has plaintext data for us */
- if (!shuffle_tls_read_plaintext(page))
- return false;
- /* handle may have grown */
- HLock(pageh);
- page = *pageh;
- status &= ~0x10;
- }
-
- if (status & 0x8) {
- /* tls can read plaintext from us */
- if (!shuffle_tls_send_plaintext(page, plainspace))
- return false;
- status &= ~0x8;
- }
-
- if (status & 0x4) {
- /* tls can read ciphertext from tcp */
- if (!shuffle_read_tcp_ciphertext(page, cipherspace))
- return false;
- status &= ~0x4;
- }
-
- if (status) {
- browser_statusf(page->browser, "TLS status is 0x%x?", status);
- return false;
- }
+ } else {
+ tlen = MIN(gemini->message_len, *len);
+ *buf = gemini->message;
+ *len = tlen;
}
+ HUnlock(pageh);
+
return true;
}
static bool
-shuffle_read_tls_ciphertext(struct page *page)
+gemini_fetch_tls_read_callback(struct tls_request *request,
+ struct browser *browser, void *cookie, char *buf, size_t len)
{
- struct gemini_request *req;
- size_t len;
- short err;
- unsigned char *buf;
+ page_handle pageh;
+ struct page *page;
+ size_t tlen;
- /* read ciphertext from TLS and send it out TCP */
-
- req = (struct gemini_request *)(page->fetch_cookie);
-
- len = scsi_tls_read(req->tls_id, &buf, 0, true);
- if (len == 0 || buf == NULL) {
- browser_statusf(page->browser,
- "Error: No ciphertext read from TLS when expected to");
+ if (cookie == NULL)
return false;
- }
-
- BROWSER_DEBUGF((page->browser,
- "Read %lu bytes of TLS ciphertext, forwarding to TCP", len));
-
- if (req->tcp_done_reading)
- return true;
- memset(&req->tcp_wds, 0, sizeof(req->tcp_wds));
- req->tcp_wds[0].ptr = (Ptr)buf;
- req->tcp_wds[0].length = len;
-
- err = _TCPSend(&req->tcp_iopb, req->tcp_stream, req->tcp_wds, NULL,
- NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPSend failed: %d", err);
+ pageh = (page_handle)cookie;
+ HLock(pageh);
+ page = *pageh;
+
+ if (page == NULL) {
+ HUnlock(pageh);
return false;
}
- return true;
-}
-
-static bool
-shuffle_read_tcp_ciphertext(struct page *page, short space)
-{
- struct gemini_request *req;
- size_t len, n;
- short err;
- unsigned short slen;
-
- /* read ciphertext from TCP and send it to TLS */
-
- req = (struct gemini_request *)(page->fetch_cookie);
-
- if (req->tcp_input_len == sizeof(req->tcp_input) ||
- req->tcp_done_reading)
- goto forward_ciphertext;
-
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- req->tcp_done_reading = true;
- browser_statusf(page->browser, "Error: Bad TCPStatus: %d", err);
- goto forward_ciphertext;
+ if (page->content_len + len >= page->content_size) {
+ page = browser_grow_page_content(page, len);
+ if (page == NULL)
+ return false;
}
- if (req->tcp_status_pb.connectionState != ConnectionStateEstablished) {
- BROWSER_DEBUGF((page->browser, "TCP connection closed (state %d)",
- req->tcp_status_pb.connectionState));
- req->tcp_done_reading = true;
- goto forward_ciphertext;
- }
- if (req->tcp_status_pb.amtUnreadData == 0)
- goto forward_ciphertext;
-
- slen = MIN(req->tcp_status_pb.amtUnreadData,
- sizeof(req->tcp_input) - req->tcp_input_len);
- if (!slen) {
- browser_statusf(page->browser,
- "Error: No buffer space available in tcp_input?");
- goto forward_ciphertext;
- }
+ memcpy(page->content + page->content_len, buf, len);
+ page->content_len += len;
+ browser_statusf(browser, "Read %ld bytes", page->content_len);
+ HUnlock(pageh);
- err = _TCPRcv(&req->tcp_iopb, req->tcp_stream,
- (Ptr)(req->tcp_input + req->tcp_input_len), &slen, NULL, NULL,
- false);
- if (err) {
- browser_statusf(page->browser, "Error: Failed TCPRcv: %d", err);
- goto forward_ciphertext;
- }
- req->tcp_input_len += slen;
- BROWSER_DEBUGF((page->browser,
- "Read %d bytes of TCP ciphertext, forwarding to TLS", slen));
-
-forward_ciphertext:
- if (!req->tcp_input_len || !space)
- return true;
-
- slen = req->tcp_input_len;
- if (slen > space)
- slen = space;
- BROWSER_DEBUGF((page->browser,
- "Forwarding %d bytes of TCP ciphertext to TLS", slen));
- len = scsi_tls_write(req->tls_id, req->tcp_input, slen, true);
- if (len == 0) {
- browser_statusf(page->browser,
- "Error: Failed forwarding %d bytes of ciphertext to TLS", slen);
- return false;
- }
-
- if (len == req->tcp_input_len)
- req->tcp_input_len = 0;
- else {
- memmove(req->tcp_input, req->tcp_input + len,
- req->tcp_input_len - len);
- req->tcp_input_len -= len;
- }
- BROWSER_DEBUGF((page->browser,
- "Wrote %ld bytes of TCP ciphertext to TLS, %ld left", len,
- req->tcp_input_len));
return true;
}
-static bool
-shuffle_tls_send_plaintext(struct page *page, short space)
+bool
+gemini_fetch(struct page *page)
{
page_handle pageh;
- struct gemini_request *req;
- size_t slen, len;
+ struct gemini_request *gemini;
+ short n;
+ bool ret;
- /* send any plaintext from us to TLS */
-
- req = (struct gemini_request *)(page->fetch_cookie);
-
- if (req->message_len == 0)
- return true;
-
- if (req->state == REQ_STATE_NEGOTIATING) {
- browser_statusf(page->browser, "Negotiated, sending request...");
- req->state = REQ_STATE_SENDING_REQUEST;
- } else if (req->state != REQ_STATE_SENDING_REQUEST) {
- browser_statusf(page->browser, "In bogus state (%d) instead of "
- "SENDING_REQUEST, disconnecting", req->state);
+ gemini = (struct gemini_request *)(page->fetch_cookie);
+ if (gemini == NULL)
return false;
- }
- slen = req->message_len;
- if (slen > space)
- slen = space;
- len = scsi_tls_write(req->tls_id, (unsigned char *)req->message, slen,
- false);
- if (!len) {
- browser_statusf(page->browser,
- "Error: Failed sending %ld bytes of plaintext to TLS", slen);
- return false;
- }
+ pageh = (page_handle)RecoverHandle(page);
- if (len == req->message_len)
- req->message_len = 0;
- else {
- memmove(req->message, req->message + len,
- req->message_len - len);
- req->message_len -= len;
- }
- BROWSER_DEBUGF((page->browser, "Wrote %ld bytes of plaintext to TLS, "
- "%ld left", len, req->message_len));
- return true;
-}
-
-static bool
-shuffle_tls_read_plaintext(struct page *page)
-{
- page_handle pageh;
- struct gemini_request *req;
- size_t len;
- unsigned char *buf;
+ ret = request_tls_shuffle(&gemini->tls, page->browser,
+ gemini_fetch_tls_read_callback, pageh,
+ gemini_fetch_tls_write_callback, pageh);
- /* read as much plaintext from TLS as we can buffer */
-
- req = (struct gemini_request *)(page->fetch_cookie);
-
- len = scsi_tls_read(req->tls_id, &buf, PAGE_CONTENT_CHUNK_SIZE, false);
- if (len == 0)
- return true;
-
- if (page->content_len + len >= page->content_size) {
- page = browser_grow_page_content(page, len);
- if (page == NULL)
- return false;
- req = (struct gemini_request *)(page->fetch_cookie);
- }
-
- memcpy(page->content + page->content_len, buf, len);
- page->content_len += len;
+ HUnlock(pageh);
- browser_statusf(page->browser, "Read %ld bytes", page->content_len);
-
- return true;
+ return ret;
}
static bool
@@ -489,11 +213,16 @@ gemini_process(struct page *page)
page_handle pageh;
size_t n, trail, skip, len;
char c;
- bool newline;
+ bool newline, ret;
pageh = (page_handle)RecoverHandle(page);
+ if (page->content_pos == page->content_len)
+ return (page->fetch_cookie != NULL);
+
handle_state:
+ ret = true;
+
switch (page->parse_state) {
case PARSE_STATE_HEADER: {
short status;
@@ -537,8 +266,9 @@ handle_state:
case PARSE_STATE_GEMTEXT:
TVAutoCalc(page->browser->output_tv, false);
- for (n = page->content_pos; n <= page->content_len; n++) {
- if (!(n == page->content_len || page->content[n] == '\n'))
+ for (n = page->content_pos; n < page->content_len; n++) {
+ if (page->content[n] != '\n' &&
+ !(n == page->content_len - 1 && page->fetch_cookie == NULL))
continue;
len = n - page->content_pos + 1;
@@ -546,7 +276,7 @@ handle_state:
skip = 0;
newline = false;
- if (n < page->content_len && page->content[n] == '\n') {
+ if (page->content[n] == '\n') {
len--;
trail = 1;
newline = true;
@@ -720,14 +450,23 @@ print_line:
if (len)
browser_print(page->browser,
page->content + page->content_pos + skip, len);
-
+
page->content_pos += skip + len + trail;
+
if (!(page->browser->style & STYLE_PRE))
page->browser->style = STYLE_NONE;
if (newline)
browser_print(page->browser, "\r", 1);
}
+ if (page->fetch_cookie == NULL &&
+ page->content_pos < page->content_len) {
+ browser_print(page->browser, page->content + page->content_pos,
+ page->content_len - page->content_pos);
+ page->content_pos = page->content_len;
+ ret = false;
+ }
+
TVAutoCalc(page->browser->output_tv, true);
TVCalcLines(page->browser->output_tv);
TVUpdate(page->browser->output_tv);
@@ -736,7 +475,7 @@ print_line:
break;
}
- return true;
+ return ret;
}
static bool
--- gopher.c Wed Nov 6 12:57:26 2024
+++ gopher.c Wed Nov 6 22:07:39 2024
@@ -19,20 +19,14 @@
#include <string.h>
#include "detritus.h"
-#include "browser.h"
#define GOPHER_PORT 70
static const char showable_types[] = "013i";
struct gopher_request {
- unsigned char tcp_buf[(4 * 1500) + 2048]; /* 4*MTU + tcp_input */
+ struct tcp_request tcp;
- TCPiopb tcp_iopb;
- StreamPtr tcp_stream;
- wdsEntry tcp_wds[2];
- TCPStatusPB tcp_status_pb;
-
char selector[URI_MAX_PATH_LEN + 3];
size_t selector_len;
};
@@ -62,21 +56,18 @@ gopher_parse_uri(char *uristr)
bool
gopher_init_request(struct page *page)
{
- struct gopher_request *req = NULL;
- size_t len;
- char ip_s[12 + 3 + 1], *filename;
- short err;
- ip_addr ip, local_ip;
- tcp_port port, local_port;
+ struct gopher_request *gopher = NULL;
+ char *filename;
if (page->content_len) {
/* already fetched, nothing to do here but reset */
page->content_pos = 0;
+ browser_commit_to_loading_page(page->browser);
return true;
}
- req = xmalloczero(sizeof(struct gopher_request));
- if (req == NULL) {
+ gopher = xmalloczero(sizeof(struct gopher_request));
+ if (gopher == NULL) {
warn("Out of memory");
return false;
}
@@ -84,44 +75,12 @@ gopher_init_request(struct page *page)
if (page->uri->port == 0)
page->uri->port = GOPHER_PORT;
- browser_statusf(page->browser, "Resolving \"%s\"...",
- page->uri->hostname);
-
- err = DNSResolveName(page->uri->hostname, &ip, NULL);
- if (err) {
- browser_statusf(page->browser, "Error: Failed resolving: %d", err);
+ if (!request_tcp_connect(&gopher->tcp, page->browser,
+ page->uri->hostname, page->uri->port)) {
+ xfree(&gopher);
return false;
}
- long2ip(ip, (char *)&ip_s);
- browser_statusf(page->browser, "Connecting to %s port %d...", ip_s,
- page->uri->port);
-
- err = _TCPCreate(&req->tcp_iopb, &req->tcp_stream, (Ptr)req->tcp_buf,
- sizeof(req->tcp_buf), NULL, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPCreate failed: %d", err);
- return false;
- }
-
- err = _TCPActiveOpen(&req->tcp_iopb, req->tcp_stream, ip,
- page->uri->port, &local_ip, &local_port, NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed connecting to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- return false;
- }
-
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser,
- "Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
- page->uri->hostname, ip_s, page->uri->port, err);
- return false;
- }
-
/*
* If we have a path other than /, assume the first character is the
* type and should not be sent, per RFC 4266.
@@ -129,41 +88,37 @@ gopher_init_request(struct page *page)
if (page->uri->path[0] == '\0' ||
(page->uri->path[0] == '/' && page->uri->path[1] == '\0')) {
page->type[0] = '1';
- req->selector_len = snprintf(req->selector, sizeof(req->selector),
- "\r\n");
+ gopher->selector_len = snprintf(gopher->selector,
+ sizeof(gopher->selector), "\r\n");
} else {
/* /0/blah -> blah */
page->type[0] = page->uri->path[1];
- req->selector_len = snprintf(req->selector, sizeof(req->selector),
- "%s\r\n", page->uri->path + 2);
+ gopher->selector_len = snprintf(gopher->selector,
+ sizeof(gopher->selector), "%s\r\n", page->uri->path + 2);
}
browser_statusf(page->browser, "Connected to %s, sending request...",
page->uri->hostname);
- memset(&req->tcp_wds, 0, sizeof(req->tcp_wds));
- req->tcp_wds[0].ptr = (Ptr)req->selector;
- req->tcp_wds[0].length = req->selector_len;
-
- err = _TCPSend(&req->tcp_iopb, req->tcp_stream, req->tcp_wds, NULL,
- NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: TCPSend failed: %d", err);
+ if (!request_tcp_send(&gopher->tcp, page->browser, gopher->selector,
+ gopher->selector_len)) {
+ request_tcp_cleanup(&gopher->tcp);
+ xfree(&gopher);
return false;
}
+
+ page->fetch_cookie = gopher;
- page->fetch_cookie = req;
-
if (strchr(showable_types, page->type[0]) == NULL) {
- if (req->selector_len == 2)
+ if (gopher->selector_len == 2)
filename = NULL;
else {
filename = strrchr(page->uri->path + 2, '/');
if (filename && filename[0] == '/')
filename++;
- if (!browser_start_download(page->browser, filename))
- return false;
}
+ if (!browser_start_download(page->browser, filename))
+ return false;
} else
browser_commit_to_loading_page(page->browser);
@@ -173,91 +128,66 @@ gopher_init_request(struct page *page)
void
gopher_cleanup(struct page *page)
{
- struct gopher_request *req;
+ struct gopher_request *gopher;
- if (page->fetch_cookie == NULL)
+ gopher = (struct gopher_request *)(page->fetch_cookie);
+ if (gopher == NULL)
return;
-
- req = (struct gopher_request *)(page->fetch_cookie);
- if (req->tcp_stream)
- _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false);
-
+ request_tcp_cleanup(&gopher->tcp);
xfree(&page->fetch_cookie);
}
bool
gopher_fetch(struct page *page)
{
- struct gopher_request *req;
+ struct gopher_request *gopher;
+ ssize_t len;
+ size_t wlen;
+ char *buf;
short err;
- unsigned short slen;
- unsigned long llen, ollen;
- Ptr rcvptr;
- if (page->fetch_cookie == NULL)
+ gopher = (struct gopher_request *)(page->fetch_cookie);
+ if (gopher == NULL)
return false;
- req = (struct gopher_request *)(page->fetch_cookie);
-
- if (req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) {
- BROWSER_DEBUGF((page->browser, "TCP I/O Result %d, disconnecting",
- req->tcp_iopb.ioResult));
+ len = request_tcp_avail(&gopher->tcp, page->browser);
+ if (len < -1)
return false;
- }
-
- err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb,
- NULL, NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: Bad TCPStatus: %d", err);
- return false;
- }
-
- if (req->tcp_status_pb.connectionState !=
- ConnectionStateEstablished) {
- BROWSER_DEBUGF((page->browser, "TCP connection closed (state %d)",
- req->tcp_status_pb.connectionState));
- return false;
- }
-
- slen = req->tcp_status_pb.amtUnreadData;
- if (slen == 0)
+ if (len == 0)
return true;
if (page->download_frefnum) {
- rcvptr = (Ptr)(page->content);
- if (slen > page->content_size)
- slen = page->content_size;
+ if (len > page->content_size)
+ len = page->content_size;
+ buf = page->content;
} else {
- if (page->content_len + slen >= page->content_size) {
- page = browser_grow_page_content(page, slen);
+ if (page->content_len + len >= page->content_size) {
+ page = browser_grow_page_content(page, len);
if (page == NULL)
return false;
- req = (struct gopher_request *)(page->fetch_cookie);
+ gopher = (struct gopher_request *)(page->fetch_cookie);
}
- rcvptr = (Ptr)(page->content + page->content_len);
+ buf = page->content + page->content_len;
}
- err = _TCPRcv(&req->tcp_iopb, req->tcp_stream, rcvptr, &slen, NULL,
- NULL, false);
- if (err) {
- browser_statusf(page->browser, "Error: Failed TCPRcv: %d", err);
+ len = request_tcp_read(&gopher->tcp, page->browser, buf, len);
+ if (len < 0)
return false;
- }
-
+
if (page->download_frefnum) {
- ollen = llen = slen;
- err = FSWrite(page->download_frefnum, &llen, page->content);
- if (err || ollen != llen) {
- warn("Failed to write: %d", err);
+ wlen = len;
+ err = FSWrite(page->download_frefnum, &wlen, page->content);
+ if (err || wlen != len) {
+ warn("Failed to write %ld bytes: %d", len, err);
return false;
}
- page->download_len += llen;
+ page->download_len += wlen;
browser_statusf(page->browser, "Wrote %ld bytes",
page->download_len);
} else {
- page->content_len += slen;
+ page->content_len += len;
browser_statusf(page->browser, "Read %ld bytes", page->content_len);
if (strchr(showable_types, page->type[0]) &&
--- main.c Tue Nov 5 20:47:53 2024
+++ main.c Wed Nov 6 22:06:06 2024
@@ -18,7 +18,6 @@
#include <string.h>
#include "detritus.h"
-#include "browser.h"
#include "focusable.h"
#include "tcp.h"
#include "util.h"
@@ -242,172 +241,4 @@ void
handle_exit(void)
{
focusables_quit();
-}
-
-struct URI *
-parse_uri(char *uristr, char *restrict_protocol)
-{
- static char protocol[URI_MAX_PROTOCOL_LEN + 1];
- static char hostname[URI_MAX_HOSTNAME_LEN + 1];
- static char path[URI_MAX_PATH_LEN + 1];
- static char str[URI_MAX_STR_LEN + 1];
- struct URI *uri;
- char *data;
- size_t protocol_len, hostname_len, path_len, str_len, size;
- short count;
- unsigned short port;
-
- /* protocol://host/path */
- if (count = 0, sscanf(uristr,
- "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
- STR(URI_MAX_HOSTNAME_LEN) "[^/]%"
- STR(URI_MAX_PATH_LEN) "[ -~]%n", /* %s stops at whitespace, use all vis */
- &protocol, &hostname, &path, &count) == 3 && count > 10)
- goto parse_ok;
-
- /* protocol://host/ */
- if (count = 0, sscanf(uristr,
- "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
- STR(URI_MAX_HOSTNAME_LEN) "[^/]/%n",
- &protocol, &hostname, &count) == 2 && count > 10) {
- path[0] = '/';
- path[1] = '\0';
- goto parse_ok;
- }
-
- /* gemini://host */
- if (count = 0, sscanf(uristr,
- "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
- STR(URI_MAX_HOSTNAME_LEN) "[^/]%n",
- &protocol, &hostname, &count) == 2 && count > 10) {
- path[0] = '/';
- path[1] = '\0';
- goto parse_ok;
- }
-
- /* failed */
- return NULL;
-
-parse_ok:
- if (restrict_protocol != NULL &&
- strcasecmp(restrict_protocol, protocol) != 0)
- return NULL;
-
- protocol_len = strlen(protocol);
- hostname_len = strlen(hostname);
- path_len = strlen(path);
- str_len = protocol_len + 3 + hostname_len + path_len;
-
- size = sizeof(struct URI) + protocol_len + 1 + hostname_len + 1 +
- path_len + 1 + str_len + 1;
- uri = xmalloc(size);
- if (uri == NULL)
- return NULL;
-
- uri->port = 0;
-
- data = (char *)uri + sizeof(struct URI);
-
- uri->protocol = data;
- memcpy(uri->protocol, protocol, protocol_len);
- uri->protocol[protocol_len] = '\0';
- data += protocol_len + 1;
-
- uri->hostname = data;
- memcpy(uri->hostname, hostname, hostname_len);
- uri->hostname[hostname_len] = '\0';
- data += hostname_len + 1;
-
- /* TODO: cut at ? and put that in query */
- uri->path = data;
- memcpy(uri->path, path, path_len);
- uri->path[path_len] = '\0';
- data += path_len + 1;
-
- uri->str = data;
- data += snprintf(uri->str, str_len + 1, "%s://%s%s",
- uri->protocol, uri->hostname, uri->path);
- data++;
-
- if (data > (char *)uri + size)
- panic("URI overflow");
-
- return uri;
-}
-
-struct URI *
-build_relative_uri(struct URI *uri, char *relative, size_t len)
-{
- static char path[URI_MAX_PATH_LEN + 1];
- static char str[URI_MAX_STR_LEN + 1];
- size_t slen, plen, n;
-
- /* http://a.b/c + //d/e -> http://d/e */
- if (relative[0] == '/' && relative[1] == '/') {
- /* retain protocol, new host and path */
- slen = snprintf(str, sizeof(str), "%s://", uri->protocol);
- while (slen < sizeof(str) - 1 && len) {
- str[slen - 1] = *relative++;
- slen++;
- }
- str[slen] = '\0';
-
- return parse_uri(str, NULL);
- }
-
- /* http://a.b/c + /d/e -> http://a.b/d/e */
- if (relative[0] == '/') {
- /* retain protocol and host, new path */
- slen = snprintf(str, sizeof(str), "%s://%s", uri->protocol,
- uri->hostname);
- while (slen < sizeof(str) - 1 && len) {
- str[slen] = *relative++;
- slen++;
- }
- str[slen] = '\0';
-
- return parse_uri(str, NULL);
- }
-
- for (n = 0; n <= len; n++) {
- /* http://a.b/c + gemini://d/e -> gemini://d/e */
- if (n < len - 2 && relative[n] == ':' && relative[n + 1] == '/' &&
- relative[n + 2] == '/') {
- /* protocol found, this isn't relative */
- if (len >= sizeof(str))
- len = sizeof(str) - 1;
- memcpy(str, relative, len);
- str[len] = '\0';
- return parse_uri(str, NULL);
- }
-
- /* http://a.b/c/d.html + e/f.html -> http://a.b/c/e/f.html */
- if (relative[n] == '/' || n == len) {
- /* remove last component in uri path up to slash */
- plen = strlcpy(path, uri->path, sizeof(path));
- while (plen && path[plen - 1] != '/') {
- path[plen - 1] = '\0';
- plen--;
- }
-
- if (plen == 0) {
- path[0] = '/';
- path[1] = '\0';
- plen = 1;
- }
-
- if (plen >= sizeof(path) - 1)
- plen = sizeof(path) - 2;
-
- memcpy(path + plen, relative, len);
- path[plen + len] = '\0';
- snprintf(str, sizeof(str), "%s://%s%s", uri->protocol,
- uri->hostname, path);
- return parse_uri(str, NULL);
- }
- }
-
- /* what the heck is this? */
- Debugger();
- return parse_uri(relative, NULL);
}
--- request.c Wed Nov 6 20:15:15 2024
+++ request.c Wed Nov 6 22:07:45 2024
@@ -0,0 +1,617 @@
+/*
+ * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "detritus.h"
+
+enum {
+ REQ_STATE_NEGOTIATING = 1,
+ REQ_STATE_SENDING_REQUEST,
+ REQ_STATE_PARSING_RESPONSE
+};
+
+static bool request_tls_read_tls_ciphertext(struct tls_request *request,
+ struct browser *browser);
+static bool request_tls_read_tcp_ciphertext(struct tls_request *request,
+ struct browser *browser, short space);
+static bool request_tls_send_plaintext(struct tls_request *request,
+ struct browser *browser, size_t space, request_tls_writer writer,
+ void *cookie);
+static bool request_tls_read_plaintext(struct tls_request *request,
+ struct browser *browser, request_tls_reader reader, void *cookie);
+
+struct URI *
+parse_uri(char *uristr, char *restrict_protocol)
+{
+ static char protocol[URI_MAX_PROTOCOL_LEN + 1];
+ static char hostname[URI_MAX_HOSTNAME_LEN + 1];
+ static char path[URI_MAX_PATH_LEN + 1];
+ static char str[URI_MAX_STR_LEN + 1];
+ struct URI *uri;
+ char *data;
+ size_t protocol_len, hostname_len, path_len, str_len, size;
+ short count;
+ unsigned short port;
+
+ /* protocol://host/path */
+ if (count = 0, sscanf(uristr,
+ "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
+ STR(URI_MAX_HOSTNAME_LEN) "[^/]%"
+ STR(URI_MAX_PATH_LEN) "[ -~]%n", /* %s stops at whitespace, use all vis */
+ &protocol, &hostname, &path, &count) == 3 && count > 10)
+ goto parse_ok;
+
+ /* protocol://host/ */
+ if (count = 0, sscanf(uristr,
+ "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
+ STR(URI_MAX_HOSTNAME_LEN) "[^/]/%n",
+ &protocol, &hostname, &count) == 2 && count > 10) {
+ path[0] = '/';
+ path[1] = '\0';
+ goto parse_ok;
+ }
+
+ /* gemini://host */
+ if (count = 0, sscanf(uristr,
+ "%" STR(URI_MAX_PROTOCOL_LEN) "[^:]://%"
+ STR(URI_MAX_HOSTNAME_LEN) "[^/]%n",
+ &protocol, &hostname, &count) == 2 && count > 10) {
+ path[0] = '/';
+ path[1] = '\0';
+ goto parse_ok;
+ }
+
+ /* failed */
+ return NULL;
+
+parse_ok:
+ if (restrict_protocol != NULL &&
+ strcasecmp(restrict_protocol, protocol) != 0)
+ return NULL;
+
+ protocol_len = strlen(protocol);
+ hostname_len = strlen(hostname);
+ path_len = strlen(path);
+ str_len = protocol_len + 3 + hostname_len + path_len;
+
+ size = sizeof(struct URI) + protocol_len + 1 + hostname_len + 1 +
+ path_len + 1 + str_len + 1;
+ uri = xmalloc(size);
+ if (uri == NULL)
+ return NULL;
+
+ uri->port = 0;
+
+ data = (char *)uri + sizeof(struct URI);
+
+ uri->protocol = data;
+ memcpy(uri->protocol, protocol, protocol_len);
+ uri->protocol[protocol_len] = '\0';
+ data += protocol_len + 1;
+
+ uri->hostname = data;
+ memcpy(uri->hostname, hostname, hostname_len);
+ uri->hostname[hostname_len] = '\0';
+ data += hostname_len + 1;
+
+ /* TODO: cut at ? and put that in query */
+ uri->path = data;
+ memcpy(uri->path, path, path_len);
+ uri->path[path_len] = '\0';
+ data += path_len + 1;
+
+ uri->str = data;
+ data += snprintf(uri->str, str_len + 1, "%s://%s%s",
+ uri->protocol, uri->hostname, uri->path);
+ data++;
+
+ if (data > (char *)uri + size)
+ panic("URI overflow");
+
+ return uri;
+}
+
+struct URI *
+build_relative_uri(struct URI *uri, char *relative, size_t len)
+{
+ static char path[URI_MAX_PATH_LEN + 1];
+ static char str[URI_MAX_STR_LEN + 1];
+ size_t slen, plen, n;
+
+ /* http://a.b/c + //d/e -> http://d/e */
+ if (relative[0] == '/' && relative[1] == '/') {
+ /* retain protocol, new host and path */
+ slen = snprintf(str, sizeof(str), "%s://", uri->protocol);
+ while (slen < sizeof(str) - 1 && len) {
+ str[slen - 1] = *relative++;
+ slen++;
+ }
+ str[slen] = '\0';
+
+ return parse_uri(str, NULL);
+ }
+
+ /* http://a.b/c + /d/e -> http://a.b/d/e */
+ if (relative[0] == '/') {
+ /* retain protocol and host, new path */
+ slen = snprintf(str, sizeof(str), "%s://%s", uri->protocol,
+ uri->hostname);
+ while (slen < sizeof(str) - 1 && len) {
+ str[slen] = *relative++;
+ slen++;
+ }
+ str[slen] = '\0';
+
+ return parse_uri(str, NULL);
+ }
+
+ for (n = 0; n <= len; n++) {
+ /* http://a.b/c + gemini://d/e -> gemini://d/e */
+ if (n < len - 2 && relative[n] == ':' && relative[n + 1] == '/' &&
+ relative[n + 2] == '/') {
+ /* protocol found, this isn't relative */
+ if (len >= sizeof(str))
+ len = sizeof(str) - 1;
+ memcpy(str, relative, len);
+ str[len] = '\0';
+ return parse_uri(str, NULL);
+ }
+
+ /* http://a.b/c/d.html + e/f.html -> http://a.b/c/e/f.html */
+ if (relative[n] == '/' || n == len) {
+ /* remove last component in uri path up to slash */
+ plen = strlcpy(path, uri->path, sizeof(path));
+ while (plen && path[plen - 1] != '/') {
+ path[plen - 1] = '\0';
+ plen--;
+ }
+
+ if (plen == 0) {
+ path[0] = '/';
+ path[1] = '\0';
+ plen = 1;
+ }
+
+ if (plen >= sizeof(path) - 1)
+ plen = sizeof(path) - 2;
+
+ memcpy(path + plen, relative, len);
+ path[plen + len] = '\0';
+ snprintf(str, sizeof(str), "%s://%s%s", uri->protocol,
+ uri->hostname, path);
+ return parse_uri(str, NULL);
+ }
+ }
+
+ /* what the heck is this? */
+ Debugger();
+ return parse_uri(relative, NULL);
+}
+
+bool
+request_tcp_connect(struct tcp_request *request, struct browser *browser,
+ char *hostname, unsigned short port)
+{
+ size_t len;
+ char ip_s[12 + 3 + 1];
+ short err;
+ ip_addr ip, local_ip;
+ tcp_port local_port;
+
+ browser_statusf(browser, "Resolving \"%s\"...", hostname);
+
+ err = DNSResolveName(hostname, &ip, NULL);
+ if (err) {
+ browser_statusf(browser, "Error: Failed resolving: %d", err);
+ request_tcp_cleanup(request);
+ return false;
+ }
+
+ long2ip(ip, (char *)&ip_s);
+ browser_statusf(browser, "Connecting to %s port %d...", ip_s, port);
+
+ err = _TCPCreate(&request->iopb, &request->stream, (Ptr)request->buf,
+ sizeof(request->buf), NULL, NULL, NULL, false);
+ if (err) {
+ browser_statusf(browser, "Error: TCPCreate failed: %d", err);
+ request_tcp_cleanup(request);
+ return false;
+ }
+
+ err = _TCPActiveOpen(&request->iopb, request->stream, ip, port,
+ &local_ip, &local_port, NULL, NULL, false);
+ if (err) {
+ browser_statusf(browser,
+ "Error: Failed connecting to %s (%s) port %d: %d",
+ hostname, ip_s, port, err);
+ request_tcp_cleanup(request);
+ return false;
+ }
+
+ err = _TCPStatus(&request->iopb, request->stream, &request->status_pb,
+ NULL, NULL, false);
+ if (err) {
+ browser_statusf(browser,
+ "Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
+ hostname, ip_s, port, err);
+ request_tcp_cleanup(request);
+ return false;
+ }
+
+ return true;
+}
+
+void
+request_tcp_cleanup(struct tcp_request *request)
+{
+ if (request->stream) {
+ _TCPRelease(&request->iopb, request->stream, NULL, NULL, false);
+ request->stream = 0;
+ }
+}
+
+bool
+request_tcp_send(struct tcp_request *request, struct browser *browser,
+ char *data, size_t len)
+{
+ short err;
+
+ memset(&request->wds, 0, sizeof(request->wds));
+ request->wds[0].ptr = (Ptr)data;
+ request->wds[0].length = len;
+
+ err = _TCPSend(&request->iopb, request->stream, request->wds, NULL,
+ NULL, false);
+ if (err) {
+ browser_statusf(browser, "Error: TCPSend failed: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+ssize_t
+request_tcp_avail(struct tcp_request *request, struct browser *browser)
+{
+ short err;
+
+ if (request->iopb.ioResult > 0 || CommandPeriodPressed()) {
+ BROWSER_DEBUGF((browser, "TCP I/O Result %d, disconnecting",
+ request->iopb.ioResult));
+ return -1;
+ }
+
+ err = _TCPStatus(&request->iopb, request->stream, &request->status_pb,
+ NULL, NULL, false);
+ if (err) {
+ browser_statusf(browser, "Error: Bad TCPStatus: %d", err);
+ return -1;
+ }
+
+ if (request->status_pb.connectionState != ConnectionStateEstablished) {
+ BROWSER_DEBUGF((browser, "TCP connection closed (state %d)",
+ request->status_pb.connectionState));
+ return -1;
+ }
+
+ return request->status_pb.amtUnreadData;
+}
+
+ssize_t
+request_tcp_read(struct tcp_request *request, struct browser *browser,
+ char *buf, size_t len)
+{
+ short err;
+ unsigned short slen;
+
+ slen = len;
+ err = _TCPRcv(&request->iopb, request->stream, (Ptr)buf, &slen, NULL,
+ NULL, false);
+ if (err) {
+ browser_statusf(browser, "Error: Failed TCPRcv: %d", err);
+ return -1;
+ }
+
+ return slen;
+}
+
+/*
+ * TLS over TCP using BlueSCSI TLS Accelerator
+ */
+
+bool
+request_tls_connect(struct tls_request *request, struct browser *browser,
+ char *hostname, unsigned short port, short flags)
+{
+ struct tls_init_request tls_req;
+ unsigned long time;
+
+ if (!scsi_can_do_tls()) {
+ warn("No TLS accelerator found, cannot make TLS requests");
+ return false;
+ }
+
+ if (!request_tcp_connect(&request->tcp, browser, hostname, port))
+ return false;
+
+ browser_statusf(browser,
+ "Connected to %s, performing TLS negotiation...", hostname);
+
+ memset(&tls_req, 0, sizeof(tls_req));
+ strlcpy(tls_req.hostname, hostname, sizeof(tls_req.hostname));
+
+ time = MAC_TO_UNIX_TIME(Time);
+ tls_req.unix_time[0] = (time >> 24) & 0xff;
+ tls_req.unix_time[1] = (time >> 16) & 0xff;
+ tls_req.unix_time[2] = (time >> 8) & 0xff;
+ tls_req.unix_time[3] = (time) & 0xff;
+
+ tls_req.flags[1] = flags;
+
+ request->tls_state = REQ_STATE_NEGOTIATING;
+ request->tls_id = scsi_tls_init(&tls_req);
+
+ if (request->tls_id == 0) {
+ browser_statusf(browser, "TLS handshake failed!");
+ request_tcp_cleanup(&request->tcp);
+ return false;
+ }
+
+ return true;
+}
+
+void
+request_tls_cleanup(struct tls_request *request)
+{
+ if (request->tls_id)
+ scsi_tls_close(request->tls_id);
+
+ request_tcp_cleanup(&request->tcp);
+}
+
+bool
+request_tls_shuffle(struct tls_request *request, struct browser *browser,
+ request_tls_reader reader, void *reader_cookie,
+ request_tls_writer writer, void *writer_cookie)
+{
+ size_t len;
+ short status, cipherspace, plainspace, tls_error;
+ bool final;
+
+ status = scsi_tls_status(request->tls_id, &cipherspace, &plainspace,
+ &tls_error);
+
+ final = false;
+ while (status != 0) {
+ if (status & 0x1) {
+ /* closed */
+ if (final) {
+ if (tls_error != 0)
+ browser_statusf(browser,
+ "Error: TLS handshake failed: %d (TLS status 0x%x)",
+ tls_error, status);
+ return false;
+ }
+ final = true;
+ }
+
+ if ((status & 0x10) || final) {
+ /* tls has plaintext data for us */
+ if (!request_tls_read_plaintext(request, browser, reader,
+ reader_cookie))
+ return false;
+ status &= ~0x10;
+ }
+
+ if (status & 0x2) {
+ /* tls has ciphertext for tcp */
+ if (!request_tls_read_tls_ciphertext(request, browser))
+ return false;
+ status &= ~0x2;
+ }
+
+ if (status & 0x8) {
+ /* tls can read plaintext from us */
+ if (!request_tls_send_plaintext(request, browser, plainspace,
+ writer, writer_cookie))
+ return false;
+ status &= ~0x8;
+ }
+
+ if (status & 0x4) {
+ /* tls can read ciphertext from tcp */
+ if (!request_tls_read_tcp_ciphertext(request, browser,
+ cipherspace))
+ return false;
+ status &= ~0x4;
+ }
+
+ if (final)
+ continue;
+
+ if (status) {
+ browser_statusf(browser, "TLS status is 0x%x?", status);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+request_tls_read_tls_ciphertext(struct tls_request *request,
+ struct browser *browser)
+{
+ size_t len;
+ unsigned char *buf;
+
+ /* read ciphertext from TLS and send it out TCP */
+
+ if (request->tcp_done_reading)
+ return false;
+
+ if (request_tcp_avail(&request->tcp, browser) == -1) {
+ request->tcp_done_reading = true;
+ return false;
+ }
+
+ /* this will point buf to scsi's static buffer */
+ len = scsi_tls_read(request->tls_id, &buf, 0, true);
+ if (len == 0 || buf == NULL) {
+ browser_statusf(browser,
+ "Error: No ciphertext read from TLS when expected to");
+ return false;
+ }
+
+ BROWSER_DEBUGF((browser,
+ "Read %lu bytes of TLS ciphertext, forwarding to TCP", len));
+
+ /* result ignored? */
+ request_tcp_send(&request->tcp, browser, (char *)buf, len);
+
+ return true;
+}
+
+static bool
+request_tls_read_tcp_ciphertext(struct tls_request *request,
+ struct browser *browser, short space)
+{
+ size_t len, n;
+ ssize_t slen;
+ short err;
+
+ /* read ciphertext from TCP and send it to TLS */
+
+ if (request->tcp.input_len == sizeof(request->tcp.input) ||
+ request->tcp_done_reading)
+ goto forward_ciphertext;
+
+ slen = request_tcp_avail(&request->tcp, browser);
+ if (slen < 0)
+ request->tcp_done_reading = true;
+ if (slen <= 0)
+ goto forward_ciphertext;
+
+ slen = MIN(slen, sizeof(request->tcp.input) - request->tcp.input_len);
+ if (!slen) {
+ browser_statusf(browser,
+ "Error: No buffer space available in TCP input?");
+ goto forward_ciphertext;
+ }
+
+ slen = request_tcp_read(&request->tcp, browser,
+ (char *)(request->tcp.input + request->tcp.input_len), slen);
+ if (slen < 0)
+ goto forward_ciphertext;
+
+ request->tcp.input_len += slen;
+ BROWSER_DEBUGF((browser,
+ "Read %ld bytes of TCP ciphertext, forwarding to TLS", slen));
+
+forward_ciphertext:
+ if (!request->tcp.input_len || !space)
+ return true;
+
+ slen = MIN(request->tcp.input_len, space);
+ BROWSER_DEBUGF((page->browser,
+ "Forwarding %ld bytes of TCP ciphertext to TLS", slen));
+
+ len = scsi_tls_write(request->tls_id, request->tcp.input, slen, true);
+ if (len == 0) {
+ browser_statusf(browser,
+ "Error: Failed forwarding %ld bytes of ciphertext to TLS", slen);
+ return false;
+ }
+
+ if (len == request->tcp.input_len)
+ request->tcp.input_len = 0;
+ else {
+ memmove(request->tcp.input, request->tcp.input + len,
+ request->tcp.input_len - len);
+ request->tcp.input_len -= len;
+ }
+ BROWSER_DEBUGF((browser,
+ "Wrote %ld bytes of TCP ciphertext to TLS, %ld left", len,
+ request->tcp.input_len));
+ return true;
+}
+
+static bool
+request_tls_send_plaintext(struct tls_request *request,
+ struct browser *browser, size_t space, request_tls_writer writer,
+ void *cookie)
+{
+ size_t olen;
+ char *data;
+ size_t len;
+
+ /* send any plaintext from us to TLS */
+
+ /* writer will set data and len to message content */
+ len = space;
+ if (!writer(request, browser, cookie, false, &data, &len))
+ return false;
+ if (len == 0 || data == NULL)
+ return true;
+
+ if (request->tls_state == REQ_STATE_NEGOTIATING) {
+ browser_statusf(browser, "Negotiated, sending request...");
+ request->tls_state = REQ_STATE_SENDING_REQUEST;
+ } else if (request->tls_state != REQ_STATE_SENDING_REQUEST) {
+ browser_statusf(browser, "In bogus state (%d) instead of "
+ "SENDING_REQUEST, disconnecting", request->tls_state);
+ return false;
+ }
+
+ olen = MIN(len, space);
+ len = scsi_tls_write(request->tls_id, (unsigned char *)data, olen,
+ false);
+ if (!len) {
+ browser_statusf(browser,
+ "Error: Failed sending %ld bytes of plaintext to TLS", olen);
+ return false;
+ }
+
+ /* inform that we wrote len bytes */
+ writer(request, browser, cookie, true, NULL, &len);
+
+ BROWSER_DEBUGF((page->browser, "Wrote %ld bytes of plaintext to TLS",
+ len));
+ return true;
+}
+
+static bool
+request_tls_read_plaintext(struct tls_request *request,
+ struct browser *browser, request_tls_reader reader, void *cookie)
+{
+ size_t len;
+ unsigned char *buf;
+
+ /* read as much plaintext from TLS as we can buffer */
+
+ /* this will point buf to scsi's static buffer */
+ len = scsi_tls_read(request->tls_id, &buf, 0, false);
+ if (len == 0)
+ return true;
+
+ if (!reader(request, browser, cookie, (char *)buf, len))
+ return false;
+
+ return true;
+}
--- request.h Wed Nov 6 17:55:41 2024
+++ request.h Wed Nov 6 22:02:37 2024
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __REQUEST_H__
+#define __REQUEST_H__
+
+#include <stdarg.h>
+
+#include "detritus.h"
+/* TODO: don't pass browser for statusf but return specific error codes? */
+#include "browser.h"
+#include "tcp.h"
+
+extern uint8_t tls_req_last_id;
+
+struct tls_init_request {
+ uint8_t flags[2];
+#define BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY (1 << 0)
+ uint8_t unix_time[4];
+ char hostname[256];
+};
+
+struct URI {
+#define URI_MAX_PROTOCOL_LEN 20
+ char *protocol;
+#define URI_MAX_HOSTNAME_LEN 255
+ char *hostname;
+#define URI_MAX_PATH_LEN 512
+ char *path;
+#define URI_MAX_STR_LEN (URI_MAX_PROTOCOL_LEN + 3 + URI_MAX_HOSTNAME_LEN + \
+ URI_MAX_PATH_LEN)
+ char *str;
+ unsigned short port;
+};
+
+struct tcp_request {
+ unsigned char buf[(4 * 1500) + 2048]; /* 4*MTU + input */
+ unsigned char input[1500];
+ unsigned long input_len;
+
+ TCPiopb iopb;
+ StreamPtr stream;
+ wdsEntry wds[2];
+ TCPStatusPB status_pb;
+};
+
+struct tls_request {
+ uint8_t tls_id;
+ short tls_state;
+ struct tcp_request tcp;
+ bool tcp_done_reading;
+};
+
+struct URI * parse_uri(char *uristr, char *restrict_protocol);
+struct URI * build_relative_uri(struct URI *uri, char *relative, size_t len);
+
+/* tcp functions */
+bool request_tcp_connect(struct tcp_request *request,
+ struct browser *browser, char *hostname, unsigned short port);
+void request_tcp_cleanup(struct tcp_request *request);
+bool request_tcp_send(struct tcp_request *request, struct browser *browser,
+ char *data, size_t len);
+ssize_t request_tcp_avail(struct tcp_request *request,
+ struct browser *browser);
+ssize_t request_tcp_read(struct tcp_request *request,
+ struct browser *browser, char *buf, size_t len);
+
+/* tls functions */
+short scsi_find_tls(void);
+uint8_t scsi_tls_init(struct tls_init_request *req);
+bool scsi_tls_close(uint8_t tls_id);
+short scsi_tls_status(uint8_t tls_id, short *cipherspace,
+ short *plainspace, short *error);
+size_t scsi_tls_read(uint8_t tls_id, unsigned char **buf, size_t max_size,
+ bool cipher);
+size_t scsi_tls_write(uint8_t tls_id, unsigned char *buf, size_t buf_size,
+ bool cipher);
+void scsi_cleanup(void);
+bool scsi_can_do_tls(void);
+
+bool request_tls_connect(struct tls_request *request,
+ struct browser *browser, char *hostname, unsigned short port, short flags);
+void request_tls_cleanup(struct tls_request *request);
+
+/*
+ * writer is called to set buf and len, then it's written, then it's called
+ * again with wrote set and size set to the actual bytes written
+ */
+typedef bool (*request_tls_writer)(struct tls_request *request,
+ struct browser *browser, void *cookie, bool wrote, char **buf,
+ size_t *len);
+
+/* reader consumes len bytes from buf */
+typedef bool (*request_tls_reader)(struct tls_request *request,
+ struct browser *browser, void *cookie, char *buf, size_t len);
+
+bool request_tls_shuffle(struct tls_request *request,
+ struct browser *browser, request_tls_reader reader, void *reader_cookie,
+ request_tls_writer writer, void *writer_cookie);
+
+#endif
\ No newline at end of file
--- scsi.c Mon Nov 4 11:11:57 2024
+++ scsi.c Wed Nov 6 22:01:36 2024
@@ -17,6 +17,7 @@
#include <SCSI.h>
#include <string.h>
#include <stdio.h>
+
#include "detritus.h"
struct scsi_inquiry {
--- tcp.c Tue Oct 1 15:09:07 2024
+++ tcp.c Wed Nov 6 22:06:36 2024
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
#include "tcp.h"
#define RCV_BUFFER_SIZE 1024
--- tcp.h Fri Aug 30 09:56:02 2024
+++ tcp.h Wed Nov 6 22:07:06 2024
@@ -1,12 +1,12 @@
/*
* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees
*/
-
-#include "MacTCP.h"
-#include "dnr.h"
#ifndef __TCP_H__
#define __TCP_H__
+
+#include "MacTCP.h"
+#include "dnr.h"
typedef struct
{