AmendHub

Download:

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; }