• by pobrn on 7/19/2023, 6:04:33 PM

    It's a nice project, but - I guess as is tradition with the majority of C projects - it has resource leaks and buffer overflows. There is at least one resource leak, namely, `sfd` is not closed when certain `WARN()` invocations jump back to the `start` label; for example, when `gethostbyname()` fails (i.e. try a non-existent domain and observe that the sockets remain open with `lsof`). (It also seems to be leaked in the happy path, so presumably `SSL_set_fd()` does not take ownership.) And if the user simply presses enter, i.e. the input is an empty line, there is a buffer underflow in line 55 as `j` will be -1 initially.

    Also

        addr.sin_addr.s_addr = *((unsigned long*)he->h_addr_list[i]);
    
    is a potential buffer overflow where `long` is 64 bits since only the first four bytes of `h_addr_list[i]` can be accessed, and also potentially misaligned for `unsigned long`; and it will also not work correctly on big endian platforms where `long` is 64 bits. Using `memcpy()` would have avoided all these problems. I am really confused as to how you arrived at the conclusion that "yes, this is the way to copy 4 bytes from A to B".

    This sounds rude, I know, and I apologize; I don't want to single you/this project out personally. I am just frustrated that even today there are people who work on C/C++ projects seemingly without having made GCC's -fanalyzer/asan/ubsan/valgrind/etc. an important part of their development workflow.

  • by __s on 7/19/2023, 6:39:21 PM

    Nice to see someone else fall upon what I call funnel-based control flow, as opposed to stack-based control flow of most programs using functions. I used the same pattern in nobox: https://github.com/serprex/nobox/blob/master/nobox.c

    Idea is that using goto your program can be mostly a loop where you make forward jumps to anywhere in the code & everything converges or jumps back to the start. It ends up making for very terse code since you're no longer passing values back & forth as much through structs/params/returns. You align variables & jump to code. Each label has a kind of ABI where the variables are the registers

  • by kazinator on 7/19/2023, 10:23:49 PM

    That program has made me realize there exists a single stylistic justification for 8 space tabs: you can fit up to six character goto labels into the indentation margins; you can put a label on any statement without adding a line.

    :)

  • by jeremycw on 7/19/2023, 5:07:13 PM

    https://github.com/ir33k/gmi100/blob/master/gmi100.c#L27 definitely threw me for a loop until I realized it was a line saving trick. It would be more readable to save lines elsewhere by exploiting the comma operator instead of essentially cramming irrelevant statements into a conditional.

    For example:

      addr.sin_family = AF_INET;
      addr.sin_port = htons(1965);
    
    Could become:

      addr.sin_family = AF_INET, addr.sin_port = htons(1965);

  • by anthk on 7/19/2023, 8:38:01 PM

    I remember gplaces, but it doesn't compile under OpenBSD. It needs lots of fixes to be built on systems beside GNU/Linux.

    On golang, Bombadillo today supports "images" with Unicode-art and searching inside the "pages" a la ctrl-f in browsers, so it's better than Amfora and one of the best TUI clients ever. Also, it does gopher and https thru external tools like lynx.

  • by sjmulder on 7/19/2023, 3:54:06 PM

    Impressive, the code is dense but not too hard to follow. And you even managed to cram in history!

  • by Narishma on 7/19/2023, 3:49:34 PM

    What do you mean by web client? From the github it looks more like a command line program.

  • by snvzz on 7/19/2023, 4:58:08 PM

    Nice, except the dependency on openssl.

    It would have been a great opportunity to use BearSSL[0].

    0. https://bearssl.org/

  • by sbjs on 7/19/2023, 6:36:15 PM

    This is the most unreadable code I may have ever seen. Congrats on successfully writing it and getting it to work.

  • by marginalia_nu on 7/19/2023, 6:09:14 PM

    Arthur Whitney, is that you? [1]

    [1] https://code.jsoftware.com/wiki/Essays/Incunabulum

  • by keepamovin on 7/20/2023, 1:04:52 AM

    This is cool. I asked ChatGPT4 to explain to me what it does, and it gives a useful overview:

    This is a minimal Gemini protocol client written in C. The Gemini protocol is an application-level internet protocol for serving hypertext documents over secure connections.

    The code includes various standard C libraries and the OpenSSL library for secure connections. The `WARN` macro definition is used to print warning messages to `stderr` and jump to the `start:` label.

    In the `main` function, it defines variables and initializes certain structs, like `struct sockaddr_in addr` that is used for internet socket addresses. It then opens a connection with the Gemini server on port 1965 (the standard port for the Gemini protocol). The connection is encrypted using the OpenSSL library functions. It also opens two file streams, one for history (`.gmi100`) and another for a temporary file. The file names and locations are generated by the `tmpnam()` function.

    The main loop reads commands from `stdin`. Depending on the command, it can execute system commands (when the input starts with '!'), navigate through the history (when input starts with a digit), process a variety of single-letter commands (like 'q' for quit, 'r' for refresh, 'c' to print current URI, 'u' for up a level, 'b' for back in history), or interpret the input as a URL or a part of a URL to be navigated to.

    For URL navigation, it creates a TCP socket connection with the Gemini server, sets up an SSL connection over that socket, and sends a request to the server for the resource identified by the URL. The client handles the server response depending on the status code. For example, if the status code is '1' it's a request for input and if it's '3' it's a redirect. It prints the response to `stdout` and also stores it in the temporary file.

    For `text/*` type responses, it additionally parses any lines that start with '=>' as links and assigns them a number, storing this in the temporary file for later navigation. If the line starts with '=>' but doesn't have a description, it goes back to reading the rest of the description.

    After processing the server response, it uses the `less` command to display the content of the temporary file, allowing the user to scroll through it if it's longer than the screen. Before each new iteration of the main loop, it flushes the temporary file and goes back to the `start:` label. If the user quits, the function returns 0 and the program terminates.

    Overall, this program is a command-line based Gemini client that allows a user to navigate the geminispace, issue queries, and view text documents served over the Gemini protocol. It also keeps a history of visited URIs in the `.gmi100` file.

    source: https://sharegpt.com/c/xscNWg6