Gophermoon 0.1 - a Gopher-Server in Lua – Part 1
About
I'm writing a simple Gopher-Server in Lua. I want to play around with the Gopher Protocol, Systemd and Linux-Container and learn some Lua-Programming on the way.
Gopher is a document retrieval protocol that had been around the same time the world-wide-web was born, but it is much simpler that HTTP and HTML. It works with plain text and directories, and the protocol on the wire is ultra-simple.
The server will be named gophermoon
.
Gopher (the protocol) is documented in RFC 1436 https://www.ietf.org/rfc/rfc1436.txt with some extension defined as Gopher+ in http://iubio.bio.indiana.edu/soft/util/gopher/Gopher+-spec.text.
Preparing systemd Socket-Activation
In the beginning, I will use Systemd to do the network part of the protocol (listening on port 70). The Lua program will just read and write to standard-input and -output.
Here are the Service-Unit and the Socket-Activation-Unit for
Systemd. The Gophermoon Service is in
/etc/systemd/system/gophermoon@.service
[Unit] Description=GopherMoon Gopher Server in La [Service] ExecStart=-/bin/lua /usr/local/sbin/gophermoon StandardInput=socket
- and the Socket-Unit is stored in
/etc/systemd/system/gophermoon.socket
[Unit] Description=GopherMoon - Gopher-Server in Lua [Socket] ListenStream=70 Accept=yes [Install] WantedBy=sockets.target
the Gopher-Server
Below is a very simple Lua-Script implementing a Hello-World Gopher-Service. It just emits the lines for a static welcome message.
A Gopher-Server waits for a connection and reads the path the client sends (ignored for now). Then it writes the Gopher-Menue out. Each Menu-Line has five fields. The first field is 1 character wide, the other fields are separated by the tabulator character (/t). Each line is terminated by a CRLF sequence.
A line with a single dot "." marks the end of the communication, server and client will close the connection.
--- Gopher Moon Version 0.1 --- a simple Gopher Server in Lua --- with some help from Systemd socket activation path = io.read() io.write("iWelcome to GopherMoon @ defaultroutes.de\t\tblog.defaultroutes.de\t70\r\n") io.write("i----------------------------------------\t\tblog.defaultroutes.de\t70\r\n") io.write(".\r\n")
starting the thing …
Systemd needs to know about the new unit-files, so we do a reload:
systemctl daemon-reload
Now we can enable and start the socket (no need to start/enable the service, as it will be started once a connection to Port 70 is made).
systemctl enable --now gophermoon.socket
The open socket on the Gopher-Port 70 now visible:
# lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 40u IPv6 3989631 0t0 TCP *:gopher (LISTEN) [...]
And test …
Gopher is simple, it can be tested with the telnet
command:
# telnet blog.defaultroutes.de 70 Trying 5.45.107.88... Connected to blog.defaultroutes.de. Escape character is '^]'. iWelcome to GopherMoon @ defaultroutes.de blog.defaultroutes.de 70 i---------------------------------------- blog.defaultroutes.de 70 . Connection closed by foreign host.
Figure 1: test with the lynx
https://lynx.browser.org/
Figure 2: test with OmniWeb https://www.omnigroup.com/more (MacOS X)