jcs
/detritus
/amendments
/30
*: Shuffle things around yet again
Now TCP and TLS connections use the same callbacks so a module
doesn't have to do different things for plaintext or TLS.
Also reduce the amount of non-protocol-specific logic that has to be
in each protocol module and reduce duplication.
jcs made amendment 30 about 1 year ago
--- browser.c Sat Nov 9 11:34:53 2024
+++ browser.c Mon Nov 11 16:05:39 2024
@@ -32,16 +32,16 @@
static Rect zerorect = { 0, 0, 0, 0 };
static Pattern fill_pattern;
-extern struct uri_handler finger_handler;
-extern struct uri_handler gemini_handler;
-extern struct uri_handler gopher_handler;
-extern struct uri_handler http_handler;
+extern struct page_handler finger_handler;
+extern struct page_handler gemini_handler;
+extern struct page_handler gopher_handler;
+extern struct page_handler http_handler;
-struct uri_handler * uri_handlers[] = {
+struct page_handler * page_handlers[] = {
&finger_handler,
&gemini_handler,
&gopher_handler,
- &http_handler,
+ //&http_handler,
};
bool browser_close(struct focusable *focusable);
@@ -67,36 +67,37 @@ void
browser_idle(struct focusable *focusable, EventRecord *event)
{
struct browser *browser = (struct browser *)focusable->cookie;
- struct uri_handler *handler;
+ struct page_handler *handler;
struct page *page;
bool ret;
TEIdle(browser->uri_te);
- if (!browser->loading_page)
- return;
-
- HLock(browser->loading_page);
- page = *(browser->loading_page);
- handler = page->handler;
-
- ret = true;
- if (page->fetch_cookie != NULL || page->download_frefnum) {
- ret = handler->fetch(page);
-
- /* handle may have grown/moved during fetch */
+ if (browser->loading_page) {
HLock(browser->loading_page);
page = *(browser->loading_page);
handler = page->handler;
+
+ ret = true;
+ if (page->request != NULL) {
+ ret = request_data_shuffle(page->request,
+ handler->request_data_queuer, browser->loading_page,
+ handler->request_data_consumer, browser->loading_page);
+
+ /* handle may have grown/moved during fetch */
+ HLock(browser->loading_page);
+ page = *(browser->loading_page);
+ handler = page->handler;
+ }
+
+ if (page->download_frefnum == 0)
+ ret &= handler->process(browser->loading_page);
+
+ if (!ret)
+ browser_finished_loading(browser);
+
+ HUnlock(browser->loading_page);
}
-
- if (!page->download_frefnum)
- ret &= handler->process(page);
-
- if (!ret)
- browser_finished_loading(browser);
-
- HUnlock(browser->loading_page);
}
struct browser *
@@ -255,9 +256,7 @@ browser_close(struct focusable *focusable)
while (fpage) {
HLock(fpage);
tpage = (*fpage)->fwd_page;
- (*fpage)->handler->cleanup(*fpage);
- /* re-lock */
- HLock(fpage);
+ (*fpage)->handler->request_cleanup(fpage);
xfree(&(*fpage)->uri);
DisposeHandle(fpage);
fpage = tpage;
@@ -267,18 +266,14 @@ browser_close(struct focusable *focusable)
while (bpage) {
HLock(bpage);
tpage = (*bpage)->back_page;
- (*bpage)->handler->cleanup(*bpage);
- /* re-lock */
- HLock(bpage);
+ (*bpage)->handler->request_cleanup(bpage);
xfree(&(*bpage)->uri);
DisposeHandle(bpage);
bpage = tpage;
}
HLock(pageh);
- (*pageh)->handler->cleanup(*pageh);
- /* re-lock */
- HLock(pageh);
+ (*pageh)->handler->request_cleanup(pageh);
xfree(&(*pageh)->uri);
DisposeHandle(pageh);
}
@@ -309,10 +304,10 @@ browser_finished_loading(struct browser *browser)
}
if ((*(browser->loading_page))->download_frefnum) {
+ download = true;
FSClose((*(browser->loading_page))->download_frefnum);
browser_statusf(browser, "Downloaded (%ld bytes)",
(*(browser->loading_page))->download_len);
- download = true;
}
committed = (browser->loading_page == browser->current_page);
@@ -326,7 +321,7 @@ browser_finished_loading(struct browser *browser)
/* browser_commit_to_loading_page never called */
Debugger();
- /* release some memory if we can */
+ /* release some wasted memory if we can */
if ((*(browser->current_page))->content_size >
(*(browser->current_page))->content_len) {
size = (*(browser->current_page))->content_len;
@@ -351,16 +346,18 @@ browser_stop_loading_page(struct browser *browser)
return;
page = *(browser->loading_page);
- page->handler->cleanup(page);
if ((*(browser->loading_page))->download_frefnum)
FSClose((*(browser->loading_page))->download_frefnum);
+
+ if (page->request)
+ page->handler->request_cleanup(browser->loading_page);
/* only dispose if uncommitted */
- if (browser->current_page == browser->loading_page)
+ if (browser->current_page == browser->loading_page) {
HUnlock(browser->loading_page);
- else {
- xfree(&(*(browser->loading_page))->uri);
+ } else {
+ xfree(&page->uri);
DisposeHandle(browser->loading_page);
}
@@ -702,17 +699,13 @@ browser_go(struct browser *browser, short dir)
browser->loading_page = opage;
browser_update_buttons(browser);
- if (!(*opage)->handler->init(*opage)) {
- (*opage)->handler->cleanup(*opage);
- HUnlock(opage);
- browser->loading_page = NULL;
- browser_update_buttons(browser);
- return;
+ if ((*opage)->handler->reset) {
+ (*opage)->handler->reset(opage);
+ } else {
+ (*opage)->content_pos = 0;
+ browser_commit_to_loading_page(browser);
}
- (*opage)->content_pos = 0;
- browser_commit_to_loading_page(browser);
-
HUnlock(opage);
}
}
@@ -722,14 +715,14 @@ browser_create_page(struct browser *browser, char *uri
{
page_handle pageh;
struct page *page;
- struct uri_handler *handler;
+ struct page_handler *handler;
struct URI *uri;
short n;
browser_stop_loading_page(browser);
- for (n = 0; n < nitems(uri_handlers); n++) {
- handler = uri_handlers[n];
+ for (n = 0; n < nitems(page_handlers); n++) {
+ handler = page_handlers[n];
if ((uri = handler->parse_uri(uristr)) == NULL)
continue;
@@ -752,11 +745,11 @@ browser_create_page(struct browser *browser, char *uri
browser_update_buttons(browser);
HLock(pageh);
- if (!handler->init(*pageh)) {
+ if (!handler->request_init(pageh)) {
/* handler failed, no point in keeping page */
- (*pageh)->handler->cleanup(*pageh);
browser->loading_page = NULL;
browser_update_buttons(browser);
+ (*pageh)->handler->request_cleanup(pageh);
xfree(&(*pageh)->uri);
DisposeHandle(pageh);
return NULL;
@@ -945,10 +938,12 @@ browser_commit_to_loading_page(struct browser *browser
}
bool
-browser_start_download(struct browser *browser, char *filename)
+browser_start_download(struct browser *browser, char *filename,
+ char *dump_buf, size_t dump_len)
{
char buf[256], pfilename[32];
SFReply reply;
+ size_t wlen;
short error, frefnum;
if (filename) {
@@ -984,8 +979,23 @@ browser_start_download(struct browser *browser, char *
warn("Failed to truncate file %s: %d", PtoCstr(reply.fName), error);
return false;
}
+
+ if (dump_buf && dump_len) {
+ wlen = dump_len;
+ error = FSWrite(frefnum, &wlen, dump_buf);
+ if (error || wlen != dump_len) {
+ warn("Failed to write %ld bytes: %d", dump_len, error);
+ return false;
+ }
+ (*(browser->loading_page))->download_len = dump_len;
+ }
(*(browser->loading_page))->download_frefnum = frefnum;
+
+ /* subsequent fetches will use content as a temporary buffer */
+ (*(browser->loading_page))->content_pos = 0;
+ (*(browser->loading_page))->content_len = 0;
+
return true;
}
@@ -1015,4 +1025,70 @@ browser_grow_page_content(struct page *page, size_t le
/* keep locked */
return *pageh;
+}
+
+bool
+page_queue_output(struct request *request, void *cookie, char **buf,
+ size_t *len, bool did_write)
+{
+ if (did_write == false) {
+ *len = request->output_len;
+ *buf = request->output;
+ return true;
+ }
+
+ if (*len == 0)
+ return true;
+
+ if (*len < request->output_len)
+ memmove(request->output, request->output + *len,
+ request->output_len - *len);
+ request->output_len -= *len;
+
+ return true;
+}
+
+bool
+page_consume_data(struct request *request, void *cookie, char **buf,
+ size_t *len, bool did_read)
+{
+ struct page *page;
+ long wlen;
+ short err;
+
+ page = *((page_handle)cookie);
+
+ if (did_read) {
+ if (page->download_frefnum) {
+ 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 += wlen;
+ browser_statusf(page->browser, "%ld bytes", page->download_len);
+ } else {
+ page->content_len += *len;
+ browser_statusf(page->browser, "%ld bytes", page->content_len);
+ }
+ } else {
+ if (page->content_len + *len >= page->content_size) {
+ page = browser_grow_page_content(page, *len);
+ if (page == NULL)
+ return false;
+ }
+
+ *buf = page->content + page->content_len;
+ }
+
+ return true;
+}
+
+void
+page_request_cleanup(page_handle pageh)
+{
+ struct page *page = *pageh;
+
+ request_xfree(&page->request);
}
--- browser.h Thu Nov 7 16:55:32 2024
+++ browser.h Mon Nov 11 16:03:43 2024
@@ -52,19 +52,28 @@ struct page {
struct browser *browser;
page_handle back_page;
page_handle fwd_page;
+
+ struct page_handler *handler;
+ void *handler_cookie;
+
+ struct request *request;
+ void *request_cookie;
+
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;
+ size_t header_len;
+
short parse_state;
size_t server_content_len;
short server_status;
char content_type[32];
+
struct URI *redir_to;
short download_frefnum;
size_t download_len;
+
char content[];
};
@@ -91,21 +100,29 @@ struct browser {
struct browser_link *last_link;
};
-struct uri_handler {
- /* return a URI if this handler can process this string */
+struct page_handler {
+ /* return a URI object if this handler can process this URI 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);
+ /* build a request to acquire content */
+ bool (*request_init)(page_handle pageh);
- /* continue fetching content */
- bool (*fetch)(struct page *page);
+ /* queue outbound content, return false when done */
+ bool (*request_data_queuer)(struct request *request, void *cookie,
+ char **buf, size_t *len, bool did_write);
- /* parse existing content */
- bool (*process)(struct page *page);
+ /* consume inbound content, return false when done */
+ bool (*request_data_consumer)(struct request *request, void *cookie,
+ char **buf, size_t *len, bool did_read);
- /* cleanup when done loading, such as freeing request */
- void (*cleanup)(struct page *page);
+ /* free request, maybe change content before passing to parser */
+ void (*request_cleanup)(page_handle pageh);
+
+ /* parse existing content, print to browser, return false when done */
+ bool (*process)(page_handle pageh);
+
+ /* reset an existing page to be parsed again with no request */
+ void (*reset)(page_handle pageh);
};
struct browser * browser_init(void);
@@ -118,6 +135,15 @@ void browser_recalc_scrollbar(struct browser *browser)
struct page * browser_grow_page_content(struct page *page, size_t len);
void browser_go_uri(struct browser *browser, char *uristr);
void browser_commit_to_loading_page(struct browser *browser);
-bool browser_start_download(struct browser *browser, char *filename);
+bool browser_start_download(struct browser *browser, char *filename,
+ char *dump_buf, size_t dump_len);
+
+/* generic versions */
+
+bool page_queue_output(struct request *request, void *cookie, char **buf,
+ size_t *len, bool did_write);
+bool page_consume_data(struct request *request, void *cookie, char **buf,
+ size_t *len, bool did_read);
+void page_request_cleanup(page_handle pageh);
#endif
\ No newline at end of file
--- finger.c Sat Nov 9 11:38:24 2024
+++ finger.c Mon Nov 11 15:53:45 2024
@@ -22,25 +22,18 @@
#define FINGER_PORT 79
-struct finger_request {
- struct tcp_request tcp;
-
- char query[128];
- size_t query_len;
-};
-
struct URI * finger_parse_uri(char *uristr);
-bool finger_init_request(struct page *page);
-bool finger_fetch(struct page *page);
-bool finger_process(struct page *page);
-void finger_cleanup(struct page *page);
+bool finger_request_init(page_handle pageh);
+bool finger_process(page_handle pageh);
-struct uri_handler finger_handler = {
+struct page_handler finger_handler = {
finger_parse_uri,
- finger_init_request,
- finger_fetch,
+ finger_request_init,
+ page_queue_output,
+ page_consume_data,
+ page_request_cleanup,
finger_process,
- finger_cleanup,
+ NULL,
};
struct URI *
@@ -50,108 +43,53 @@ finger_parse_uri(char *uristr)
}
bool
-finger_init_request(struct page *page)
+finger_request_init(page_handle pageh)
{
- struct finger_request *finger = NULL;
-
- if (page->content_len)
- /* already fetched */
- return true;
+ struct page *page = *pageh;
+ size_t path_len;
- finger = xmalloczero(sizeof(struct finger_request));
- if (finger == NULL) {
- warn("Out of memory");
- return false;
- }
-
if (page->uri->port == 0)
page->uri->port = FINGER_PORT;
- if (!request_tcp_connect(&finger->tcp, page->browser,
- page->uri->hostname, page->uri->port)) {
- xfree(&finger);
- return false;
- }
-
- browser_statusf(page->browser, "Connected to %s, sending request...",
- page->uri->hostname);
-
if (page->uri->path[0] == '\0' ||
(page->uri->path[0] == '/' && page->uri->path[1] == '\0'))
- finger->query_len = snprintf(finger->query, sizeof(finger->query),
- "\r\n");
+ path_len = 0;
else
- finger->query_len = snprintf(finger->query, sizeof(finger->query),
- "%s\r\n", page->uri->path + 1);
+ path_len = strlen(page->uri->path + 1);
+
+ page->request = request_connect(page->browser, page->uri->hostname,
+ page->uri->port, false, 0);
+ if (page->request == NULL)
+ return false;
- if (!request_tcp_send(&finger->tcp, page->browser, finger->query,
- finger->query_len)) {
- request_tcp_cleanup(&finger->tcp);
- xfree(&finger);
+ page->request->output_len = path_len + 2;
+ page->request->output = xmalloc(page->request->output_len + 1);
+ if (page->request->output == NULL) {
+ xfree(&page->request);
+ warn("Out of memory");
return false;
}
-
- page->fetch_cookie = finger;
- browser_commit_to_loading_page(page->browser);
- return true;
-}
-
-void
-finger_cleanup(struct page *page)
-{
- struct finger_request *finger;
+ snprintf(page->request->output, page->request->output_len + 1,
+ "%s\r\n", path_len ? page->uri->path + 1 : "");
- finger = (struct finger_request *)(page->fetch_cookie);
- if (finger == NULL)
- return;
+ browser_statusf(page->browser, "Connected to %s, sending request...",
+ page->uri->hostname);
- request_tcp_cleanup(&finger->tcp);
- xfree(&page->fetch_cookie);
-}
-
-bool
-finger_fetch(struct page *page)
-{
- struct finger_request *finger;
- ssize_t len;
+ browser_commit_to_loading_page(page->browser);
- finger = (struct finger_request *)(page->fetch_cookie);
- if (finger == NULL)
- return false;
-
- len = request_tcp_avail(&finger->tcp, page->browser);
- if (len < -1)
- return 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;
- finger = (struct finger_request *)(page->fetch_cookie);
- }
-
- len = request_tcp_read(&finger->tcp, page->browser,
- page->content + page->content_len, len);
- if (len < 0)
- return false;
-
- page->content_len += len;
- browser_statusf(page->browser, "%ld bytes", page->content_len);
-
return true;
}
bool
-finger_process(struct page *page)
+finger_process(page_handle pageh)
{
+ struct page *page = *pageh;
long n;
bool ret = true;
if (page->content_pos == page->content_len)
- return (page->fetch_cookie != NULL);
+ return (page->request != NULL);
page->browser->style = STYLE_PRE;
@@ -176,8 +114,7 @@ finger_process(struct page *page)
}
}
- if (page->fetch_cookie == NULL &&
- page->content_pos < page->content_len) {
+ if (page->request == NULL && page->content_pos < page->content_len) {
/* no request to fetch more content, finish */
browser_print(page->browser, page->content + page->content_pos,
page->content_len - page->content_pos, false);
--- gemini.c Fri Nov 8 08:56:25 2024
+++ gemini.c Mon Nov 11 16:08:27 2024
@@ -28,27 +28,21 @@ enum {
PARSE_STATE_DOWNLOAD
};
-struct gemini_request {
- struct tls_request tls;
-
- char message[URI_MAX_STR_LEN + 1];
- size_t message_len;
-};
-
struct URI * gemini_parse_uri(char *uristr);
-bool gemini_init_request(struct page *page);
-bool gemini_fetch(struct page *page);
-bool gemini_process(struct page *page);
-void gemini_cleanup(struct page *page);
+bool gemini_request_init(page_handle pageh);
+bool gemini_process(page_handle pageh);
+void gemini_reset(page_handle pageh);
static bool parse_header(struct page *page, char *str, size_t len);
-struct uri_handler gemini_handler = {
+struct page_handler gemini_handler = {
gemini_parse_uri,
- gemini_init_request,
- gemini_fetch,
+ gemini_request_init,
+ page_queue_output,
+ page_consume_data,
+ page_request_cleanup,
gemini_process,
- gemini_cleanup,
+ gemini_reset,
};
struct URI *
@@ -58,158 +52,37 @@ gemini_parse_uri(char *uristr)
}
bool
-gemini_init_request(struct page *page)
+gemini_request_init(page_handle pageh)
{
+ struct page *page = *pageh;
struct gemini_request *gemini = NULL;
- if (page->content_len) {
- /* already fetched */
- page->parse_state = PARSE_STATE_BODY;
- return true;
- }
-
- gemini = xmalloczero(sizeof(struct gemini_request));
- if (gemini == NULL) {
- warn("Out of memory");
- return false;
- }
-
if (page->uri->port == 0)
page->uri->port = GEMINI_PORT;
- if (!request_tls_connect(&gemini->tls, page->browser,
- page->uri->hostname, page->uri->port,
- BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY)) {
- xfree(&gemini);
+ page->request = request_connect(page->browser, page->uri->hostname,
+ page->uri->port, true, BLUESCSI_TLS_INIT_REQUEST_FLAG_NO_VERIFY);
+ if (page->request == NULL)
return false;
- }
-
- gemini->message_len = snprintf(gemini->message,
- sizeof(gemini->message), "%s\r\n", page->uri->str);
- page->fetch_cookie = gemini;
-
- return true;
-}
-
-void
-gemini_cleanup(struct page *page)
-{
- struct gemini_request *gemini;
-
- gemini = (struct gemini_request *)(page->fetch_cookie);
- if (gemini == NULL)
- return;
-
- request_tls_cleanup(&gemini->tls);
- xfree(&page->fetch_cookie);
-}
-
-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;
- struct page *page;
- page_handle pageh;
- size_t tlen;
-
- pageh = (page_handle)cookie;
- if (pageh == NULL)
+ page->request->output_len = strlen(page->uri->str) + 2;
+ page->request->output = xmalloc(page->request->output_len + 1);
+ if (page->request->output == NULL) {
+ xfree(&page->request);
+ warn("Out of memory");
return false;
- page = *pageh;
- if (page == NULL)
- return false;
-
- gemini = (struct gemini_request *)page->fetch_cookie;
- if (gemini == NULL)
- 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;
- }
- } else {
- tlen = MIN(gemini->message_len, *len);
- *buf = gemini->message;
- *len = tlen;
}
- return true;
-}
+ snprintf(page->request->output, page->request->output_len + 1,
+ "%s\r\n", page->uri->str);
-static bool
-gemini_fetch_tls_read_callback(struct tls_request *request,
- struct browser *browser, void *cookie, char *buf, size_t len)
-{
- page_handle pageh;
- struct page *page;
- size_t tlen;
- short err;
-
- pageh = (page_handle)cookie;
- if (pageh == NULL)
- return false;
- page = *pageh;
- if (page == NULL)
- return false;
-
- if (page->parse_state == PARSE_STATE_DOWNLOAD) {
- tlen = len;
- err = FSWrite(page->download_frefnum, &tlen, buf);
- if (err || tlen != len) {
- warn("Failed to write %ld bytes: %d", len, err);
- return false;
- }
-
- page->download_len += tlen;
- browser_statusf(page->browser, "%ld bytes", page->download_len);
- } else {
- if (page->content_len + len >= page->content_size) {
- page = browser_grow_page_content(page, len);
- if (page == NULL)
- return false;
- }
-
- memcpy(page->content + page->content_len, buf, len);
- page->content_len += len;
- browser_statusf(page->browser, "%ld bytes", page->content_len);
- }
-
return true;
}
-bool
-gemini_fetch(struct page *page)
-{
- page_handle pageh;
- struct gemini_request *gemini;
- short n;
- bool ret;
-
- gemini = (struct gemini_request *)(page->fetch_cookie);
- if (gemini == NULL)
- return false;
-
- pageh = (page_handle)RecoverHandle(page);
-
- ret = request_tls_shuffle(&gemini->tls, page->browser,
- gemini_fetch_tls_read_callback, pageh,
- gemini_fetch_tls_write_callback, pageh);
-
- return ret;
-}
-
static bool
-gemini_process(struct page *page)
+gemini_process(page_handle pageh)
{
- page_handle pageh;
+ struct page *page = *pageh;
size_t n, trail, skip, len;
ssize_t tlen, link_len, title_len, j;
char *link, *title, *tcontent;
@@ -218,10 +91,8 @@ gemini_process(struct page *page)
char c, *filename;
bool newline, gemtext;
- pageh = (page_handle)RecoverHandle(page);
-
if (page->content_pos == page->content_len)
- return (page->fetch_cookie != NULL);
+ return (page->request != NULL);
if (page->parse_state == PARSE_STATE_HEADER) {
for (n = page->content_pos; n < page->content_len; n++) {
@@ -237,55 +108,30 @@ gemini_process(struct page *page)
return false;
}
+ page->header_len = n + 1;
+
/* ignore any "; charset" after type */
if (strncasecmp(page->content_type, "text/gemini", 11) == 0 ||
strncasecmp(page->content_type, "text/plain", 10) == 0) {
page->parse_state = PARSE_STATE_BODY;
browser_commit_to_loading_page(page->browser);
+ page->content_pos = page->header_len;
} else {
page->parse_state = PARSE_STATE_DOWNLOAD;
filename = strrchr(page->uri->path, '/');
if (filename && filename[0] == '/')
filename++;
-
- if (!browser_start_download(page->browser, filename))
+
+ if (!browser_start_download(page->browser, filename,
+ page->content + page->header_len,
+ page->content_len - page->header_len))
return false;
}
- page->content_pos = n + 1;
-
/* gemini only has one header */
break;
}
-
- if (page->parse_state == PARSE_STATE_BODY) {
- /* shift out the header */
- if (page->content_pos == page->content_len)
- page->content_pos = page->content_len = 0;
- else {
- memmove(page->content, page->content + page->content_pos,
- page->content_len - page->content_pos);
- page->content_len -= page->content_pos;
- page->content_pos = 0;
- }
- } else if (page->parse_state == PARSE_STATE_DOWNLOAD) {
- /* dump remaining data after header into our new file */
- tlen = len = page->content_len - page->content_pos;
- if (tlen) {
- err = FSWrite(page->download_frefnum, &tlen,
- page->content + page->content_pos);
- if (err || tlen != len) {
- warn("Failed to write %ld bytes: %d", len, err);
- return false;
- }
- page->download_len = tlen;
- }
-
- /* reset for future writing */
- page->content_pos = page->content_len = 0;
- return true;
- }
}
if (page->parse_state != PARSE_STATE_BODY)
@@ -297,7 +143,7 @@ gemini_process(struct page *page)
for (n = page->content_pos; n < page->content_len; n++) {
if (page->content[n] != '\n' &&
- !(n == page->content_len - 1 && page->fetch_cookie == NULL))
+ !(n == page->content_len - 1 && page->request == NULL))
continue;
len = n - page->content_pos + 1;
@@ -314,7 +160,7 @@ gemini_process(struct page *page)
len--;
trail++;
}
- } else if (page->fetch_cookie != NULL)
+ } else if (page->request != NULL)
/* no newline at the end and fetching, so wait for more data */
return true;
@@ -471,27 +317,40 @@ gemini_process(struct page *page)
}
print_line:
- if (len || newline)
+ if (len) {
browser_print(page->browser,
page->content + page->content_pos + skip, len, newline);
-
- page->content_pos += skip + len + trail;
-
- if (!(page->browser->style & STYLE_PRE))
+ } else if (newline) {
+ /* print blank lines in the default size */
page->browser->style = STYLE_NONE;
+ browser_print(page->browser, "\r", 1, false);
+ }
+
+ page->content_pos += skip + len + trail;
}
- if (page->fetch_cookie == NULL &&
- page->content_pos < page->content_len) {
+ if (page->request == NULL && page->content_pos < page->content_len) {
browser_print(page->browser, page->content + page->content_pos,
page->content_len - page->content_pos, false);
page->content_pos = page->content_len;
}
if (page->content_pos == page->content_len)
- return (page->fetch_cookie != NULL);
+ return (page->request != NULL);
return true;
+}
+
+void
+gemini_reset(page_handle pageh)
+{
+ struct page *page = *pageh;
+
+ /* restart at body */
+ page->parse_state = PARSE_STATE_BODY;
+ page->content_pos = page->header_len;
+
+ browser_commit_to_loading_page(page->browser);
}
static bool
--- gopher.c Sat Nov 9 11:54:06 2024
+++ gopher.c Mon Nov 11 16:03:56 2024
@@ -27,27 +27,20 @@
static const char showable_types[] = "013i";
-struct gopher_request {
- struct tcp_request tcp;
-
- char selector[URI_MAX_PATH_LEN + 3];
- size_t selector_len;
-};
-
struct URI * gopher_parse_uri(char *uristr);
-bool gopher_init_request(struct page *page);
-bool gopher_fetch(struct page *page);
-bool gopher_process(struct page *page);
-void gopher_cleanup(struct page *page);
+bool gopher_request_init(page_handle pageh);
+bool gopher_process(page_handle pageh);
+void gopher_reset(page_handle pageh);
static void gopher_print_menu(struct page *page, char *line, size_t len);
-struct uri_handler gopher_handler = {
+struct page_handler gopher_handler = {
gopher_parse_uri,
- gopher_init_request,
- gopher_fetch,
+ gopher_request_init,
+ page_queue_output,
+ page_consume_data,
+ page_request_cleanup,
gopher_process,
- gopher_cleanup,
};
struct URI *
@@ -57,32 +50,15 @@ gopher_parse_uri(char *uristr)
}
bool
-gopher_init_request(struct page *page)
+gopher_request_init(page_handle pageh)
{
- struct gopher_request *gopher = NULL;
+ struct page *page = *pageh;
+ size_t selector_len;
char *filename;
- if (page->content_len) {
- /* already fetched */
- page->parse_state = 0;
- return true;
- }
-
- gopher = xmalloczero(sizeof(struct gopher_request));
- if (gopher == NULL) {
- warn("Out of memory");
- return false;
- }
-
if (page->uri->port == 0)
page->uri->port = GOPHER_PORT;
- if (!request_tcp_connect(&gopher->tcp, page->browser,
- page->uri->hostname, page->uri->port)) {
- xfree(&gopher);
- return false;
- }
-
/*
* If we have a path other than /, assume the first character is the
* type and should not be sent, per RFC 4266.
@@ -90,36 +66,38 @@ gopher_init_request(struct page *page)
if (page->uri->path[0] == '\0' ||
(page->uri->path[0] == '/' && page->uri->path[1] == '\0')) {
page->content_type[0] = '1';
- gopher->selector_len = snprintf(gopher->selector,
- sizeof(gopher->selector), "\r\n");
+ selector_len = 0;
} else {
/* /0/blah -> blah */
page->content_type[0] = page->uri->path[1];
- gopher->selector_len = snprintf(gopher->selector,
- sizeof(gopher->selector), "%s\r\n", page->uri->path + 2);
+ selector_len = strlen(page->uri->path + 2);
}
- browser_statusf(page->browser, "Connected to %s, sending selector...",
- page->uri->hostname);
-
- if (!request_tcp_send(&gopher->tcp, page->browser, gopher->selector,
- gopher->selector_len)) {
- request_tcp_cleanup(&gopher->tcp);
- xfree(&gopher);
+ page->request = request_connect(page->browser, page->uri->hostname,
+ page->uri->port, false, 0);
+ if (page->request == NULL)
return false;
+
+ page->request->output_len = selector_len + 2;
+ page->request->output = xmalloc(page->request->output_len + 1);
+ if (page->request->output == NULL) {
+ xfree(&page->request);
+ warn("Out of memory");
+ return false;
}
-
- page->fetch_cookie = gopher;
+ snprintf(page->request->output, page->request->output_len + 1,
+ "%s\r\n", selector_len ? page->uri->path + 2 : "");
+
if (strchr(showable_types, page->content_type[0]) == NULL) {
- if (gopher->selector_len == 2)
+ if (selector_len == 2)
filename = NULL;
else {
filename = strrchr(page->uri->path + 2, '/');
if (filename && filename[0] == '/')
filename++;
}
- if (!browser_start_download(page->browser, filename))
+ if (!browser_start_download(page->browser, filename, NULL, 0))
return false;
} else
browser_commit_to_loading_page(page->browser);
@@ -127,87 +105,27 @@ gopher_init_request(struct page *page)
return true;
}
-void
-gopher_cleanup(struct page *page)
-{
- struct gopher_request *gopher;
-
- gopher = (struct gopher_request *)(page->fetch_cookie);
- if (gopher == NULL)
- return;
-
- request_tcp_cleanup(&gopher->tcp);
- xfree(&page->fetch_cookie);
-}
-
bool
-gopher_fetch(struct page *page)
+gopher_process(page_handle pageh)
{
- struct gopher_request *gopher;
- ssize_t len;
- size_t wlen;
- char *buf;
- short err;
-
- gopher = (struct gopher_request *)(page->fetch_cookie);
- if (gopher == NULL)
- return false;
+ struct page *page = *pageh;
+ long n, plines = 0;
+ bool ret = true;
- len = request_tcp_avail(&gopher->tcp, page->browser);
- if (len < -1)
- return 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;
- gopher = (struct gopher_request *)(page->fetch_cookie);
+ if (strchr(showable_types, page->content_type[0]) &&
+ page->content_len >= 3 &&
+ (page->content_len == 3 ||
+ page->content[page->content_len - 4] == '\n') &&
+ page->content[page->content_len - 3] == '.' &&
+ page->content[page->content_len - 2] == '\r' &&
+ page->content[page->content_len - 1] == '\n') {
+ /* ".\r\n" is end of transfer */
+ page->content_len -= 3;
+ ret = false;
}
- len = request_tcp_read(&gopher->tcp, page->browser,
- page->content + page->content_len, len);
- if (len < 0)
- return false;
-
- if (page->download_frefnum) {
- 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 += wlen;
- browser_statusf(page->browser, "%ld bytes", page->download_len);
- } else {
- page->content_len += len;
- browser_statusf(page->browser, "%ld bytes", page->content_len);
-
- if (strchr(showable_types, page->content_type[0]) &&
- page->content_len >= 3 &&
- (page->content_len == 3 ||
- page->content[page->content_len - 4] == '\n') &&
- page->content[page->content_len - 3] == '.' &&
- page->content[page->content_len - 2] == '\r' &&
- page->content[page->content_len - 1] == '\n') {
- /* ".\r\n" is end of transfer */
- page->content_len -= 3;
- return false;
- }
- }
-
- return true;
-}
-
-bool
-gopher_process(struct page *page)
-{
- long n, plines = 0;
- bool ret = true;
-
if (page->content_pos == page->content_len)
- return (page->fetch_cookie != NULL);
+ return (page->request != NULL);
TVAutoCalc(page->browser->output_tv, false);
@@ -223,7 +141,7 @@ gopher_process(struct page *page)
}
}
- if (page->fetch_cookie == NULL &&
+ if (page->request == NULL &&
page->content_pos < page->content_len) {
gopher_print_menu(page, page->content + page->content_pos,
page->content_len - page->content_pos);
@@ -252,7 +170,7 @@ gopher_process(struct page *page)
}
}
- if (page->fetch_cookie == NULL &&
+ if (page->request == NULL &&
page->content_pos < page->content_len) {
browser_print(page->browser, page->content + page->content_pos,
page->content_len - page->content_pos, false);
@@ -270,6 +188,16 @@ gopher_process(struct page *page)
browser_recalc_scrollbar(page->browser);
return ret;
+}
+
+void
+gopher_reset(page_handle pageh)
+{
+ struct page *page = *pageh;
+
+ page->parse_state = 0;
+ page->content_pos = 0;
+ browser_commit_to_loading_page(page->browser);
}
static void
--- request.c Fri Nov 8 08:36:56 2024
+++ request.c Mon Nov 11 13:08:11 2024
@@ -26,16 +26,22 @@ enum {
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);
+/* TCP functions */
+bool request_tcp_connect(struct request *request);
+bool request_tcp_send(struct request *request, char *data, size_t len);
+ssize_t request_tcp_avail(struct request *request);
+ssize_t request_tcp_read(struct request *request, char *buf, size_t len);
+bool request_tls_init(struct request *request);
+void request_tls_cleanup(struct tls_request *request);
+
+bool request_tls_read_tls_ciphertext(struct request *request);
+bool request_tls_read_tcp_ciphertext(struct request *request, short space);
+bool request_tls_send_plaintext(struct request *request,
+ size_t space, request_data_queuer queuer, void *cookie);
+bool request_tls_read_plaintext(struct request *request,
+ request_data_consumer consumer, void *cookie);
+
struct URI *
parse_uri(char *uristr, char *restrict_scheme)
{
@@ -49,6 +55,10 @@ parse_uri(char *uristr, char *restrict_scheme)
short count;
unsigned short port;
+ /* TODO: handle host:port */
+ /* TODO: handle //user:pass@host */
+ /* TODO: split path at ? and store query */
+
/* scheme://host/path */
if (count = 0, sscanf(uristr,
"%" STR(URI_MAX_SCHEME_LEN) "[^:]://%"
@@ -109,7 +119,6 @@ parse_ok:
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';
@@ -205,108 +214,242 @@ build_relative_uri(struct URI *uri, char *relative, si
return parse_uri(relative, NULL);
}
+struct request *
+request_connect(struct browser *browser, char *hostname,
+ unsigned short port, bool tls, unsigned char tls_flags)
+{
+ struct request *request;
+
+ request = xmalloczero(sizeof(struct request));
+ if (request == NULL)
+ return NULL;
+
+ request->browser = browser;
+ strlcpy(request->hostname, hostname, sizeof(request->hostname));
+ request->port = port;
+
+ if (!request_tcp_connect(request)) {
+ xfree(&request);
+ return NULL;
+ }
+
+ if (tls) {
+ request->tls_flags = tls_flags;
+ if (!request_tls_init(request)) {
+ xfree(&request);
+ return NULL;
+ }
+ }
+
+ return request;
+}
+
+void
+request_xfree(struct request **requestp)
+{
+ struct request *request = *requestp;
+
+ if (request->tls_id)
+ scsi_tls_close(request->tls_id);
+
+ if (request->stream) {
+ _TCPRelease(&request->iopb, request->stream, NULL, NULL, false);
+ request->stream = 0;
+ }
+
+ if (request->output)
+ xfree(&request->output);
+
+ xfree(requestp);
+}
+
bool
-request_tcp_connect(struct tcp_request *request, struct browser *browser,
- char *hostname, unsigned short port)
+request_data_shuffle(struct request *request, request_data_queuer queuer,
+ void *queuer_cookie, request_data_consumer consumer,
+ void *consumer_cookie)
{
size_t len;
- char ip_s[12 + 3 + 1];
+ ssize_t slen;
+ char *data;
+ short status, cipherspace, plainspace, tls_error;
+ bool final;
+
+ if (request->tls_id) {
+ 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(request->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, consumer,
+ consumer_cookie))
+ return false;
+ status &= ~0x10;
+ }
+
+ if (status & 0x2) {
+ /* tls has ciphertext for tcp */
+ if (!request_tls_read_tls_ciphertext(request))
+ return false;
+ status &= ~0x2;
+ }
+
+ if (status & 0x8) {
+ /* tls can read plaintext from us */
+ if (!request_tls_send_plaintext(request, plainspace,
+ queuer, queuer_cookie))
+ return false;
+ status &= ~0x8;
+ }
+
+ if (status & 0x4) {
+ /* tls can read ciphertext from tcp */
+ if (!request_tls_read_tcp_ciphertext(request, cipherspace))
+ return false;
+ status &= ~0x4;
+ }
+
+ if (final)
+ continue;
+
+ if (status) {
+ browser_statusf(request->browser,
+ "Error: TLS status is 0x%x?", status);
+ return false;
+ }
+ }
+ } else {
+ /* let the caller send out anything it has */
+ if (!queuer(request, queuer_cookie, &data, &len, false))
+ return false;
+
+ if (len > 0 && data != NULL) {
+ if (!request_tcp_send(request, data, len))
+ return false;
+
+ /* inform that we wrote len bytes */
+ if (!queuer(request, queuer_cookie, NULL, &len, true))
+ return false;
+ }
+
+ /* receive data and send it to the consumer */
+ slen = request_tcp_avail(request);
+ if (slen > 0) {
+ data = NULL;
+
+ /* get the consumer's buf and make sure it can handle slen */
+ len = slen;
+ if (!consumer(request, consumer_cookie, &data, &len, false))
+ return false;
+
+ /* read into their buf */
+ slen = request_tcp_read(request, data, len);
+ if (slen < 0)
+ return false;
+ if (slen > 0) {
+ /* and let them know we read it */
+ len = slen;
+ if (!consumer(request, consumer_cookie, &data, &len, true))
+ return false;
+ }
+ } else if (slen < 0)
+ return false;
+ }
+
+ if (request->tcp_done_reading && request->input_len == 0)
+ return false;
+
+ return true;
+}
+
+/* internal */
+
+bool
+request_tcp_connect(struct request *request)
+{
+ size_t len;
short err;
- ip_addr ip, local_ip;
+ ip_addr local_ip;
tcp_port local_port;
- browser_statusf(browser, "Resolving \"%s\"...", hostname);
+ browser_statusf(request->browser,
+ "Resolving \"%s\"...", request->hostname);
- err = DNSResolveName(hostname, &ip, NULL);
+ err = DNSResolveName(request->hostname, &request->ip, NULL);
if (err) {
- browser_statusf(browser, "Error: Failed resolving: %d", err);
- request_tcp_cleanup(request);
+ browser_statusf(request->browser,
+ "Error: Failed resolving %s: %d", request->hostname, err);
return false;
}
- long2ip(ip, (char *)&ip_s);
- browser_statusf(browser, "Connecting to %s port %d...", ip_s, port);
+ long2ip(request->ip, (char *)&request->ip_s);
+ browser_statusf(request->browser,
+ "Connecting to %s port %d...", request->ip_s, request->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);
+ browser_statusf(request->browser,
+ "Error: TCPCreate failed: %d", err);
return false;
}
- err = _TCPActiveOpen(&request->iopb, request->stream, ip, port,
- &local_ip, &local_port, NULL, NULL, false);
+ err = _TCPActiveOpen(&request->iopb, request->stream, request->ip,
+ request->port, &local_ip, &local_port, NULL, NULL, false);
if (err) {
- browser_statusf(browser,
+ browser_statusf(request->browser,
"Error: Failed connecting to %s (%s) port %d: %d",
- hostname, ip_s, port, err);
- request_tcp_cleanup(request);
+ request->hostname, request->ip_s, request->port, err);
return false;
}
err = _TCPStatus(&request->iopb, request->stream, &request->status_pb,
NULL, NULL, false);
if (err) {
- browser_statusf(browser,
+ browser_statusf(request->browser,
"Error: Failed TCPStatus on connection to %s (%s) port %d: %d",
- hostname, ip_s, port, err);
- request_tcp_cleanup(request);
+ request->hostname, request->ip_s, request->port, err);
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)
+request_tcp_avail(struct request *request)
{
short err;
if (request->iopb.ioResult > 0 || CommandPeriodPressed()) {
- BROWSER_DEBUGF((browser, "TCP I/O Result %d, disconnecting",
- request->iopb.ioResult));
+ BROWSER_DEBUGF((request->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);
+ browser_statusf(request->browser,
+ "Error: Bad TCPStatus: %d", err);
return -1;
}
if (request->status_pb.connectionState != ConnectionStateEstablished) {
- BROWSER_DEBUGF((browser, "TCP connection closed (state %d)",
+ BROWSER_DEBUGF((request->browser,
+ "TCP connection closed (state %d)",
request->status_pb.connectionState));
return -1;
}
@@ -314,9 +457,28 @@ request_tcp_avail(struct tcp_request *request, struct
return request->status_pb.amtUnreadData;
}
+bool
+request_tcp_send(struct request *request, 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(request->browser,
+ "Error: TCPSend failed: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
ssize_t
-request_tcp_read(struct tcp_request *request, struct browser *browser,
- char *buf, size_t len)
+request_tcp_read(struct request *request, char *buf, size_t len)
{
short err;
unsigned short slen;
@@ -325,7 +487,8 @@ request_tcp_read(struct tcp_request *request, struct b
err = _TCPRcv(&request->iopb, request->stream, (Ptr)buf, &slen, NULL,
NULL, false);
if (err) {
- browser_statusf(browser, "Error: Failed TCPRcv: %d", err);
+ browser_statusf(request->browser,
+ "Error: Failed TCPRcv: %d", err);
return -1;
}
@@ -337,8 +500,7 @@ request_tcp_read(struct tcp_request *request, struct b
*/
bool
-request_tls_connect(struct tls_request *request, struct browser *browser,
- char *hostname, unsigned short port, short flags)
+request_tls_init(struct request *request)
{
struct tls_init_request tls_req;
unsigned long time;
@@ -348,14 +510,11 @@ request_tls_connect(struct tls_request *request, struc
return false;
}
- if (!request_tcp_connect(&request->tcp, browser, hostname, port))
- return false;
-
- browser_statusf(browser,
- "Connected to %s, negotiating TLS...", hostname);
+ browser_statusf(request->browser,
+ "Connected to %s, negotiating TLS...", request->hostname);
memset(&tls_req, 0, sizeof(tls_req));
- strlcpy(tls_req.hostname, hostname, sizeof(tls_req.hostname));
+ strlcpy(tls_req.hostname, request->hostname, sizeof(tls_req.hostname));
time = MAC_TO_UNIX_TIME(Time);
tls_req.unix_time[0] = (time >> 24) & 0xff;
@@ -363,106 +522,23 @@ request_tls_connect(struct tls_request *request, struc
tls_req.unix_time[2] = (time >> 8) & 0xff;
tls_req.unix_time[3] = (time) & 0xff;
- tls_req.flags[1] = flags;
+ tls_req.flags[1] = request->tls_flags;
request->tls_state = REQ_STATE_NEGOTIATING;
request->tls_id = scsi_tls_init(&tls_req);
if (request->tls_id == 0) {
- browser_statusf(browser, "Error: TLS handshake failed!");
- request_tcp_cleanup(&request->tcp);
+ browser_statusf(request->browser, "Error: TLS handshake failed!");
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)
+request_tls_read_tls_ciphertext(struct request *request)
{
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, "Error: TLS status is 0x%x?", status);
- return false;
- }
- }
-
- if (request->tcp_done_reading && request->tcp.input_len == 0)
- 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 */
@@ -470,31 +546,31 @@ request_tls_read_tls_ciphertext(struct tls_request *re
if (request->tcp_done_reading)
return true;
- if (request_tcp_avail(&request->tcp, browser) < 0) {
+ if (request_tcp_avail(request) < 0) {
request->tcp_done_reading = true;
return true;
}
/* this will point buf to scsi's static buffer */
+ buf = NULL;
len = scsi_tls_read(request->tls_id, &buf, 0, true);
if (len == 0 || buf == NULL) {
- browser_statusf(browser,
+ browser_statusf(request->browser,
"Error: No ciphertext read from TLS when expected to");
return false;
}
- BROWSER_DEBUGF((browser,
+ BROWSER_DEBUGF((request->browser,
"Read %lu bytes of TLS ciphertext, forwarding to TCP", len));
/* result ignored? */
- request_tcp_send(&request->tcp, browser, (char *)buf, len);
+ request_tcp_send(request, (char *)buf, len);
return true;
}
-static bool
-request_tls_read_tcp_ciphertext(struct tls_request *request,
- struct browser *browser, short space)
+bool
+request_tls_read_tcp_ciphertext(struct request *request, short space)
{
size_t len, n;
ssize_t slen;
@@ -502,64 +578,63 @@ request_tls_read_tcp_ciphertext(struct tls_request *re
/* read ciphertext from TCP and send it to TLS */
- if (request->tcp.input_len == sizeof(request->tcp.input) ||
+ if (request->input_len == sizeof(request->input) ||
request->tcp_done_reading)
goto forward_ciphertext;
- slen = request_tcp_avail(&request->tcp, browser);
+ slen = request_tcp_avail(request);
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);
+ slen = MIN(slen, sizeof(request->input) - request->input_len);
if (!slen) {
- browser_statusf(browser,
+ browser_statusf(request->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);
+ slen = request_tcp_read(request,
+ (char *)(request->input + request->input_len), slen);
if (slen < 0)
goto forward_ciphertext;
- request->tcp.input_len += slen;
- BROWSER_DEBUGF((browser,
+ request->input_len += slen;
+ BROWSER_DEBUGF((request->browser,
"Read %ld bytes of TCP ciphertext, forwarding to TLS", slen));
forward_ciphertext:
- if (!request->tcp.input_len || !space)
+ if (!request->input_len || !space)
return true;
- slen = MIN(request->tcp.input_len, space);
- BROWSER_DEBUGF((page->browser,
+ slen = MIN(request->input_len, space);
+ BROWSER_DEBUGF((request->browser,
"Forwarding %ld bytes of TCP ciphertext to TLS", slen));
- len = scsi_tls_write(request->tls_id, request->tcp.input, slen, true);
+ len = scsi_tls_write(request->tls_id, request->input, slen, true);
if (len == 0) {
- browser_statusf(browser,
+ browser_statusf(request->browser,
"Error: Failed forwarding %ld bytes of ciphertext to TLS", slen);
return false;
}
- if (len == request->tcp.input_len)
- request->tcp.input_len = 0;
+ if (len == request->input_len)
+ request->input_len = 0;
else {
- memmove(request->tcp.input, request->tcp.input + len,
- request->tcp.input_len - len);
- request->tcp.input_len -= len;
+ memmove(request->input, request->input + len,
+ request->input_len - len);
+ request->input_len -= len;
}
- BROWSER_DEBUGF((browser,
+ BROWSER_DEBUGF((request->browser,
"Wrote %ld bytes of TCP ciphertext to TLS, %ld left", len,
- request->tcp.input_len));
+ request->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)
+bool
+request_tls_send_plaintext(struct request *request, size_t space,
+ request_data_queuer queuer, void *cookie)
{
size_t olen;
char *data;
@@ -567,19 +642,19 @@ request_tls_send_plaintext(struct tls_request *request
/* send any plaintext from us to TLS */
- /* writer will set data and len to message content */
+ /* queuer will set data and len to message content */
len = space;
- if (!writer(request, browser, cookie, false, &data, &len))
+ if (!queuer(request, cookie, &data, &len, false))
return false;
if (len == 0 || data == NULL)
return true;
- if (request->tls_state == REQ_STATE_NEGOTIATING) {
- //browser_statusf(browser, "Negotiated, sending request...");
+ if (request->tls_state == REQ_STATE_NEGOTIATING)
request->tls_state = REQ_STATE_SENDING_REQUEST;
- } else if (request->tls_state != REQ_STATE_SENDING_REQUEST) {
- browser_statusf(browser, "Error: bogus state (%d) instead of "
- "SENDING_REQUEST, disconnecting", request->tls_state);
+ else if (request->tls_state != REQ_STATE_SENDING_REQUEST) {
+ browser_statusf(request->browser,
+ "Error: bogus state (%d) instead of SENDING_REQUEST, "
+ "disconnecting", request->tls_state);
return false;
}
@@ -587,34 +662,38 @@ request_tls_send_plaintext(struct tls_request *request
len = scsi_tls_write(request->tls_id, (unsigned char *)data, olen,
false);
if (!len) {
- browser_statusf(browser,
+ browser_statusf(request->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);
+ if (!queuer(request, cookie, NULL, &len, true))
+ return false;
- BROWSER_DEBUGF((page->browser, "Wrote %ld bytes of plaintext to TLS",
- len));
+ BROWSER_DEBUGF((request->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)
+bool
+request_tls_read_plaintext(struct request *request,
+ request_data_consumer consumer, void *cookie)
{
size_t len;
- unsigned char *buf;
+ 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);
+ len = 1024;
+ if (!consumer(request, cookie, &buf, &len, false))
+ return false;
+
+ len = scsi_tls_read(request->tls_id, (unsigned char **)&buf, len, false);
if (len == 0)
return true;
- if (!reader(request, browser, cookie, (char *)buf, len))
+ if (!consumer(request, cookie, &buf, &len, true))
return false;
return true;
--- request.h Thu Nov 7 09:03:15 2024
+++ request.h Mon Nov 11 12:05:52 2024
@@ -18,6 +18,7 @@
#define __REQUEST_H__
#include <stdarg.h>
+#include "stdint.h"
#include "detritus.h"
/* TODO: don't pass browser for statusf but return specific error codes? */
@@ -46,40 +47,68 @@ struct URI {
unsigned short port;
};
-struct tcp_request {
+struct request {
unsigned char buf[(4 * 1500) + 2048]; /* 4*MTU + input */
unsigned char input[1500];
unsigned long input_len;
+ char *output;
+ unsigned long output_len;
+
TCPiopb iopb;
StreamPtr stream;
wdsEntry wds[2];
TCPStatusPB status_pb;
-};
+ bool tcp_done_reading;
-struct tls_request {
+ char hostname[URI_MAX_HOSTNAME_LEN];
+ unsigned short port;
+ ip_addr ip;
+ char ip_s[12 + 3 + 1];
+
uint8_t tls_id;
short tls_state;
- struct tcp_request tcp;
- bool tcp_done_reading;
+ unsigned char tls_flags;
+
+ struct browser *browser;
};
struct URI * parse_uri(char *uristr, char *restrict_scheme);
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);
+/*
+ * queuer is called with wrote=false to set buf and len, then data is
+ * written to socket, then queuer is called again with did_write=true and
+ * len set to the actual bytes written
+ */
+typedef bool (*request_data_queuer)(struct request *request, void *cookie,
+ char **buf, size_t *len, bool did_write);
-/* tls functions */
+/*
+ * consumer is called with did_read=false and len, and must set buf to
+ * something that can hold len bytes, then data is read into buf, then
+ * consumer is called again with did_read=true and len set to the actual
+ * bytes read
+ */
+typedef bool (*request_data_consumer)(struct request *request, void *cookie,
+ char **buf, size_t *len, bool did_read);
+
+/*
+ * shuffle handles actual tcp/tls reading and writing, then calls back to
+ * queuer and consumer when necessary
+ */
+bool request_data_shuffle(struct request *request,
+ request_data_queuer queuer, void *queuer_cookie,
+ request_data_consumer consumer, void *consumer_cookie);
+
+struct request * request_connect(struct browser *browser, char *hostname,
+ unsigned short port, bool tls, unsigned char tls_flags);
+void request_xfree(struct request **requestp);
+
+/* TLS functions */
short scsi_find_tls(void);
+void scsi_cleanup(void);
+bool scsi_can_do_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,
@@ -88,27 +117,5 @@ size_t scsi_tls_read(uint8_t tls_id, unsigned char **b
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 Wed Nov 6 22:01:36 2024
+++ scsi.c Mon Nov 11 13:12:32 2024
@@ -19,6 +19,7 @@
#include <stdio.h>
#include "detritus.h"
+#include "request.h"
struct scsi_inquiry {
unsigned char deviceType;
@@ -195,9 +196,12 @@ scsi_tls_read(uint8_t tls_id, unsigned char **buf, siz
unsigned char cdb[6];
short ret;
- if (max_size == 0 || max_size > sizeof(scsi_buf))
- max_size = sizeof(scsi_buf);
-
+ if (*buf == NULL) {
+ *buf = (unsigned char *)&scsi_buf;
+ if (max_size == 0 || max_size > sizeof(scsi_buf))
+ max_size = sizeof(scsi_buf);
+ }
+
memset(cdb, 0, sizeof(cdb));
cdb[0] = BLUESCSI_TLS_CMD;
if (cipher)
@@ -208,14 +212,12 @@ scsi_tls_read(uint8_t tls_id, unsigned char **buf, siz
cdb[3] = (max_size >> 8) & 0xff;
cdb[4] = max_size & 0xff;
- ret = scsi_io(cdb, sizeof(cdb), SCSI_READ_UNKNOWN_SIZE, scsi_buf,
- max_size);
+ ret = scsi_io(cdb, sizeof(cdb), SCSI_READ_UNKNOWN_SIZE, *buf, max_size);
if (ret < 0) {
*buf = NULL;
return 0;
}
- *buf = (unsigned char *)&scsi_buf;
return ret;
}