Strotmann.de
21 Dec 2017

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.

gophermoon-01-lynx.png

Figure 1: test with the lynx https://lynx.browser.org/

gophermoon-01-omniweb.png

Figure 2: test with OmniWeb https://www.omnigroup.com/more (MacOS X)

Other posts
Creative Commons License
strotmann.de by Carsten Strotmann is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License .