Some time ago I wrote about using my email server from Mac OS 9 and how I ended up putting stunnel on a Pi because as usual with old systems the crypto stuff was blocking. It worked but I always wanted my PowerMac G4 to do the crypto by itself.
Networking on Mac OS 9 is kinda annoying. In the past I tried GUSI which does BSD sockets but I had trouble setting it up and it doesn't feel native at all. So I went for Open Transport directly. It works but the API is not very friendly, endpoints, OTLook, flow control errors, lots of code just to open a connection and wait for an answer. I wanted something simpler.
That's why I made Dial, an attempt at a small TCP library for Mac OS 9 and CodeWarrior 6.
What does Dial do? It opens a connection, sends, receives and closes over Open Transport. With the help of BearSSL it can also do TLS which means my G4 now fetches HTTPS and Gemini pages on its own, no proxy, no Pi in the middle. Pretty cool!
Why Dial? Because of plan 9 dial(2) also short and sweet :) Dialing a server and talking to it is pretty much all this library does anyway.
Figure 1: Dialing Gemini
Everything is on my files page as StuffIt archives.
Pick what you need but BearSSL is mandatory for now even if you don't intend to use TLS.
I wanted the API to stay small and easy to grasp. Here is a complete finger client for example:
#include <stdio.h> #include "Dial.h" int main(void) { DialConn *conn; char buf[512]; long n; Dial_Init(); if(Dial_Open("typed-hole.org", 79, 15, &conn) == noErr) { Dial_SendString(conn, "julien" kDialCRLF); while((n = Dial_Receive(conn, buf, sizeof(buf) - 1)) > 0) { buf[n] = '\0'; printf("%s", buf); } Dial_Close(conn); } Dial_Shutdown(); return 0; }
Pretty straightforward I'd say.
Note the kDialCRLF, by default CodeWarrior maps \n to CR and \r to LF
(classic Mac convention apparently) so writing "\r\n" sends LF CR
instead of CR LF. kDialCRLF spells out the bytes so it's always
correct.
For TLS you load the root certificates once and open the connection
with Dial_OpenTLS instead. kDialVerifyCert means the server's
certificate has to be signed by one of the roots.
Certificates_Load("cacert.pem"); Dial_Init(); Dial_OpenTLS("lobste.rs", 443, 30, kDialVerifyCert, &conn); Dial_SendString(conn, "GET /newest.json HTTP/1.0" kDialCRLF "Host: lobste.rs" kDialCRLF kDialCRLF); total = Dial_ReceiveAll(conn, &response);
Dial_ReceiveAll reads until the server closes and gives you back a
buffer that grows as needed, just free() it when done.
Add cJSON et voilà the latest lobste.rs stories in a SIOUX window.
Figure 2: lobste.rs JSON over HTTPS
The roots come from curl's copy of Mozilla's list. Updating them is just downloading a new cacert.pem (WebOne helps here) and dropping it next to the app, no recompiling needed. Loading the 118 roots takes less than a second on my G4.
Most Gemini capsules use self-signed certificates and the protocol
recommends "trust on first use" instead of CAs.
That's what kDialAnyCert is for, the host name is still checked but
not who signed the certificate nor its dates.
Dial_OpenTLS("9til.de", 1965, 30, kDialAnyCert, &conn); Dial_SendString(conn, "gemini://9til.de/" kDialCRLF); total = Dial_ReceiveAll(conn, &response);
A Gemini request is just the URL and the answer is a status line like
20 text/gemini followed by the page. Simple enough for the G4 :p
The crypto is all BearSSL, the tricky part was the Mac glue around it: converting the Mac's local time since 1904 into the GMT date BearSSL wants and finding something random enough to seed it with and setting up certificates checks.
Other classic Mac TLS projects worth a look: macTLS, Certainly, ssheven and Crypto Ancienne. Also macTLS has a plan to implement randomness properly.
That's it for Dial, now onto the more interesting part, creating cool apps with it like TurboCastor since castor9 is already a thing :)
Figure 3: Shutting down