initial commit
This commit is contained in:
commit
d9e5170676
11 changed files with 968 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
98
Cargo.lock
generated
Normal file
98
Cargo.lock
generated
Normal file
|
@ -0,0 +1,98 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
|
||||
[[package]]
|
||||
name = "sheldon_director"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"openssl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "sheldon_director"
|
||||
version = "0.1.0"
|
||||
authors = ["Klockenschooster <slartibartfast@klockenschooster.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
openssl = { version = "0.10" }
|
||||
|
20
LICENSE.md
Normal file
20
LICENSE.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2020 Jan Wolff
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In
|
||||
no event will the authors be held liable for any damages arising from the use
|
||||
of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including
|
||||
commercial applications, and to alter it and redistribute it freely, subject to
|
||||
the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim
|
||||
that you wrote the original software. If you use this software in a product,
|
||||
an acknowledgment in the product documentation would be appreciated but is
|
||||
not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
Sheldon Director
|
||||
================
|
||||
|
||||
A Gemini Server written in Rust.
|
||||
|
||||
Why "Sheldon Director"?
|
||||
-----------------------
|
||||
|
||||
Because it is the real name of that villain in _Kim Possible_ who went by the
|
||||
pseudonym "Gemini". (https://kimpossible.fandom.com/wiki/Gemini)
|
32
doc/cert.pem
Normal file
32
doc/cert.pem
Normal file
|
@ -0,0 +1,32 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFlTCCA32gAwIBAgIUPqRiFp5Gc1b2Frbab/WcQ4Xdg74wDQYJKoZIhvcNAQEL
|
||||
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MCAX
|
||||
DTIwMDUxNDE4NDI0NVoYDzIxMjAwNDIwMTg0MjQ1WjBZMQswCQYDVQQGEwJBVTET
|
||||
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
||||
dHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||
DwAwggIKAoICAQCqDaso0s8+Rt9fILlF7koLcYJCjs+MjB0kpS7itdJj1YXW6a5l
|
||||
zIRP9bMSSftVmHGR1qFo6suKbVEayCkTrw3PU6w7iW7CMiRK6XL1Ix4B5BYEymKx
|
||||
ihA1geRQZkKL8UHfudnpq7+yTqwKXdOu4KrRUbMdIiOlaIhhK4/DZk8gxmZpiv81
|
||||
xqohtyVsr7Z8yKkSoX7TySPvn3qx83BgeoGSqe/pX4quKVKj8ylkfdyKlaVPLGvS
|
||||
ktTfr2rUYtRz1+xOEEYxUdaZBRZ2/G1QaeELWimwPTAmg9KWCfA0/6TWMDvh2Btq
|
||||
J0qJtcu43QVFf4AxoUQ/S/Mf4Zmc/3gfOcEHrjnNube0xaUfMdjErhwPmXKc0E78
|
||||
hd9jlkJaICdGSEC19DZBt8LinyTMr6+2E/YyD+bl+LUL8uI7m9zFgJYlLXUeZvab
|
||||
PvcxUq72pP6/j+9fGlT2JXSZ6bIDXyDbgz0QmZ5Rh+q5fwSAtfkSE16cx87aOM48
|
||||
iI98c4joN9FXAVwNT53ga/dxwCqQZH4Sd024DEeo2Gk5dWyPTUnENcl4vW1Twa70
|
||||
1gY60YqHw5UgH9DnBN+8yWjFHlbL4aZvbLdTsQOCkHMxoTt5vGP2e5XHh0Etk1oK
|
||||
5E7FtXOFJ125PcPZ9BlyyyaKxR9ykpMYNtHJjScQPTlMhku/8aK3sb3eRwIDAQAB
|
||||
o1MwUTAdBgNVHQ4EFgQUWrJXR3N6gnEkBQHACPj0Db+TtoAwHwYDVR0jBBgwFoAU
|
||||
WrJXR3N6gnEkBQHACPj0Db+TtoAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQsFAAOCAgEAKLhG4+kNvoVCyByrcSUU9rhUZxn1ezAQJcUO3ETZIE1lU1Z46rMk
|
||||
9he4+sPTDxEEoynL8wseAStn+CVVd8Leer5QZEWqwaM+LkcjicYr+U8CNQ1p28Gy
|
||||
oiSH0rK7RzkhRDwmnMYAx9skpem/nLj21RH4gPBUYaW5mU05O4F3vZvWZfwfGJi8
|
||||
MSvPOVUZVUIJZFqNAECKxGbzneO3iKLETnDdAnw2VgLwTcvLfkbtBxbNwEgTgV8+
|
||||
sWbmgTlPX1ToRdL2Gz5pp0hz6B94H0HoUO4yH2Afzb5O6+sKxLoIBq7S8PBURP0A
|
||||
F+mtnU/VWJBNf52KRVHLWApdtieaqHIrHt9qbWsJ2bx+P3z5SkjOJbLDo06pAp4h
|
||||
uYZuRiykUW+bMfv5Ec+qKUJVidG2J7YKpq3ghzdmebnZ+d/D/5KkEl22EnXw48Mj
|
||||
r+ynJlq+EK5jQc0Y3CRt/ggjrqvmtE5Mqb6ltoVG4/yNa8z0tjza/B17tmYaCC/W
|
||||
L7I/2KY7tKUuOk9JjWbTikTpGccn496QZlcpNWxiiK3qDOeah1iRLBqIm6HQeg5B
|
||||
Vz3YWRir/L2QbkDFy6Lxy/28C0hbEljYV5rvuKjx5a4o8nUUcOPhiDTrpCpGKLZ8
|
||||
uQ/9hYrLWWDYAREeNyuUHyyDyWGObqBr8W/vbQder2t8cSUZBhHR69k=
|
||||
-----END CERTIFICATE-----
|
52
doc/key.pem
Normal file
52
doc/key.pem
Normal file
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
Qdcfj82isy1JR/mZp2t+sasS1DGxNg==
|
||||
-----END PRIVATE KEY-----
|
649
doc/spec-spec.txt
Normal file
649
doc/spec-spec.txt
Normal file
|
@ -0,0 +1,649 @@
|
|||
-----------------------------
|
||||
Project Gemini
|
||||
"Speculative specification"
|
||||
v0.11.0, March 1st 2020
|
||||
-----------------------------
|
||||
|
||||
This is an increasingly less rough sketch of an actual spec for
|
||||
Project Gemini. Although not finalised yet, further changes to the
|
||||
specification are likely to be relatively small. You can write code
|
||||
to this pseudo-specification and be confident that it probably won't
|
||||
become totally non-functional due to massive changes next week, but
|
||||
you are still urged to keep an eye on ongoing development of the
|
||||
protocol and make changes as required.
|
||||
|
||||
This is provided mostly so that people can quickly get up to speed on
|
||||
what I'm thinking without having to read lots and lots of old phlog
|
||||
posts and keep notes.
|
||||
|
||||
Feedback on any part of this is extremely welcome, please email
|
||||
solderpunk@sdf.org.
|
||||
|
||||
-----------------------------
|
||||
|
||||
1. Overview
|
||||
|
||||
Gemini is a client-server protocol featuring request-response
|
||||
transactions, broadly similar to gopher or HTTP. Connections are
|
||||
closed at the end of a single transaction and cannot be reused. When
|
||||
Gemini is served over TCP/IP, servers should listen on port 1965 (the
|
||||
first manned Gemini mission, Gemini 3, flew in March '65). This is an
|
||||
unprivileged port, so it's very easy to run a server as a "nobody"
|
||||
user, even if e.g. the server is written in Go and so can't drop
|
||||
privileges in the traditional fashion.
|
||||
|
||||
1.1 Gemini transactions
|
||||
|
||||
There is one kind of Gemini transaction, roughly equivalent to a
|
||||
gopher request or a HTTP "GET" request. Transactions happen as
|
||||
follows:
|
||||
|
||||
C: Opens connection
|
||||
S: Accepts connection
|
||||
C/S: Complete TLS handshake (see 1.4)
|
||||
C: Validates server certificate (see 1.4.2)
|
||||
C: Sends request (one CRLF terminated line) (see 1.2)
|
||||
S: Sends response header (one CRFL terminated line), closes connection
|
||||
under non-success conditions (see 1.3.1, 1.3.2)
|
||||
S: Sends response body (text or binary data) (see 1.3.3)
|
||||
S: Closes connection
|
||||
C: Handles response (see 1.3.4)
|
||||
|
||||
1.2 Gemini requests
|
||||
|
||||
Gemini requests are a single CRLF-terminated line with the following
|
||||
structure:
|
||||
|
||||
<URL><CR><LF>
|
||||
|
||||
<URL> is a UTF-8 encoded absolute URL, of maximum length 1024 bytes.
|
||||
If the scheme of the URL is not specified, a scheme of gemini:// is
|
||||
implied.
|
||||
|
||||
Sending an absolute URL instead of only a path or selector is
|
||||
effectively equivalent to building in a HTTP "Host" header. It
|
||||
permits virtual hosting of multiple Gemini domains on the same IP
|
||||
address. It also allows servers to optionally act as proxies.
|
||||
Including schemes other than gemini:// in requests allows servers to
|
||||
optionally act as protocol-translating gateways to e.g. fetch gopher
|
||||
resources over Gemini. Proxying is optional and the vast majority of
|
||||
servers are expected to only respond to requests for resources at
|
||||
their own domain(s).
|
||||
|
||||
1.3 Responses
|
||||
|
||||
Gemini response consist of a single CRLF-terminated header line,
|
||||
optionally followed by a response body.
|
||||
|
||||
1.3.1 Response headers
|
||||
|
||||
Gemini response headers look like this:
|
||||
|
||||
<STATUS><whitespace><META><CR><LF>
|
||||
|
||||
<STATUS> is a two-digit numeric status code, as described below in
|
||||
1.3.2 and in Appendix 1.
|
||||
|
||||
<whitespace> is any non-zero number of consecutive spaces or tabs.
|
||||
|
||||
<META> is a UTF-8 encoded string of maximum length 1024, whose meaning
|
||||
is <STATUS> dependent.
|
||||
|
||||
If <STATUS> does not belong to the "SUCCESS" range of codes, then the
|
||||
server MUST close the connection after sending the header and MUST NOT
|
||||
send a response body.
|
||||
|
||||
If a server sends a <STATUS> which is not a two-digit number or a
|
||||
<META> which exceeds 1024, the client SHOULD close the connection and
|
||||
disregard the response header, informing the user of an error.
|
||||
|
||||
1.3.2 Status codes
|
||||
|
||||
Gemini uses two-digit numeric status codes. Related status codes share
|
||||
the same first digit. Importantly, the first digit of Gemini status
|
||||
codes do not group codes into vague categories like "client error" and
|
||||
"server error" as per HTTP. Instead, the first digit alone provides
|
||||
enough information for a client to determine how to handle the
|
||||
response. By design, it is possible to write a simple but feature
|
||||
complete client which only looks at the first digit. The second digit
|
||||
provides more fine-grained information, for unambiguous server logging,
|
||||
to allow writing comfier interactive clients which provide a slightly
|
||||
more streamlined user interface, and to allow writing more robust and
|
||||
intelligent automated clients like content aggregators, search engine
|
||||
crawlers, etc.
|
||||
|
||||
The first digit of a response code unambiguously places the response
|
||||
into one of six categories, which define the semantics of the <META>
|
||||
line.
|
||||
|
||||
1 INPUT
|
||||
|
||||
The requested resource accepts a line of textual user input.
|
||||
The <META> line is a prompt which should be displayed to the
|
||||
user. The same resource should then be requested again with
|
||||
the user's input included as a query component. Queries are
|
||||
included in requests as per the usual generic URL definition
|
||||
in RFC3986, i.e. separated from the path by a ?. There is no
|
||||
response body.
|
||||
|
||||
2 SUCCESS
|
||||
|
||||
The request was handled successfully and a response body will
|
||||
follow the response header. The <META> line is a MIME media
|
||||
type which applies to the response body.
|
||||
|
||||
3 REDIRECT
|
||||
|
||||
The server is redirecting the client to a new location for the
|
||||
requested resource. There is no response body. The header
|
||||
text is a new URL for the requested resource. The URL may be
|
||||
absolute or relative. The redirect should be considered
|
||||
temporary, i.e. clients should continue to request the
|
||||
resource at the original address and should not performance
|
||||
convenience actions like automatically updating bookmarks.
|
||||
There is no response body.
|
||||
|
||||
4 TEMPORARY FAILURE
|
||||
|
||||
The request has failed. There is no response body. The
|
||||
nature of the failure is temporary, i.e. an identical request
|
||||
MAY succeed in the future. The contents of <META> may provide
|
||||
additional information on the failure, and should be displayed
|
||||
to human users.
|
||||
|
||||
5 PERMANENT FAILURE
|
||||
|
||||
The request has failed. There is no response body. The
|
||||
nature of the failure is permanent, i.e. identical future
|
||||
requests will reliably fail for the same reason. The contents
|
||||
of <META> may provide additional information on the failure,
|
||||
and should be displayed to human users. Automatic clients
|
||||
such as aggregators or indexing crawlers should should not
|
||||
repeat this request.
|
||||
|
||||
6 CLIENT CERTIFICATE REQUIRED
|
||||
|
||||
The requested resource requires client-certificate
|
||||
authentication to access. If the request was made without a
|
||||
certificate, it should be repeated with one. If the request
|
||||
was made with a certificate, the server did not accept it and
|
||||
the request should be repeated with a different certificate.
|
||||
The contents of <META> may provide additional information on
|
||||
certificate requirements or the reason a certificate was
|
||||
rejected.
|
||||
|
||||
Note that for basic interactive clients for human use, errors 4 and 5
|
||||
may be effectively handled identically, by simply displaying the
|
||||
contents of <META> under a heading of "ERROR". The
|
||||
temporary/permanent error distinction is primarily relevant to
|
||||
well-behaving automated clients. Basic clients may also choose not to
|
||||
support client-certificate authentication, in which case only four
|
||||
distinct status handling routines are required (for statuses beginning
|
||||
with 1, 2, 3 or a combined 4-or-5).
|
||||
|
||||
The full two-digit system is detailed in Appendix 1. Note that for
|
||||
each of the six valid first digits, a code with a second digit of zero
|
||||
corresponds is a generic status of that kind with no special
|
||||
semantics. This means that basic servers without any advanced
|
||||
functionality need only be able to return codes of 10, 20, 30, 40 or
|
||||
50.
|
||||
|
||||
The Gemini status code system has been carefully designed so that the
|
||||
increased power (and correspondingly increased complexity) of the
|
||||
second digits is entirely "opt-in" on the part of both servers and
|
||||
clients.
|
||||
|
||||
1.3.3 Response bodies
|
||||
|
||||
Response bodies are just raw content, text or binary, ala gopher.
|
||||
There is no support for compression, chunking or any other kind of
|
||||
content or transfer encoding. The server closes the connection after
|
||||
the final byte, there is no "end of response" signal like gopher's
|
||||
lonely dot.
|
||||
|
||||
Response bodies only accompany responses whose header indicates a
|
||||
SUCCESS status (i.e. a status code whose first digit is 2). For such
|
||||
responses, <META> is a MIME media type as defined in RFC 2046.
|
||||
|
||||
If a MIME type begins with "text/" and no charset is explicitly given,
|
||||
the charset should be assumed to be UTF-8. Compliant clients MUST
|
||||
support UTF-8-encoded text/* responses. Clients MAY optionally
|
||||
support other encodings. Clients receiving a response in a charset
|
||||
they cannot decode SHOULD gracefully inform the user what happened
|
||||
instead of displaying garbage.
|
||||
|
||||
If <META> is an empty string, the MIME type MUST default to
|
||||
"text/gemini; charset=utf-8".
|
||||
|
||||
1.3.4 Response body handling
|
||||
|
||||
Response handling by clients should be informed by the provided MIME
|
||||
type information. Gemini defines one MIME type of its own
|
||||
(text/gemini) whose handling is discussed below in 1.3.5. In all
|
||||
other cases, clients should do "something sensible" based on the MIME
|
||||
type. Minimalistic clients might adopt a strategy of printing all
|
||||
other text/* responses to the screen without formatting and saving
|
||||
all non-text responses to the disk. Clients for unix systems may
|
||||
consult /etc/mailcap to find installed programs for handling non-text
|
||||
types.
|
||||
|
||||
1.3.5 text/gemini responses
|
||||
|
||||
1.3.5.1 Overview
|
||||
|
||||
In the same sense that HTML is the "native" response format of HTTP
|
||||
and plain text is the native response format of gopher, Gemini defines
|
||||
its own native response format - though of course, thanks to the
|
||||
inclusion of a MIME type in the response header Gemini can be used to
|
||||
serve plain text, rich text, HTML, Markdown, LaTeX, etc.
|
||||
|
||||
Response bodies of type "text/gemini" are a kind of lightweight
|
||||
hypertext format, which takes inspiration from gophermaps and from
|
||||
Markdown. The format permits richer typographic possibilities than
|
||||
the plain text of Gopher, but remains extremely easy to parse. The
|
||||
format is line-oriented, and a satisfactory rendering can be achieved
|
||||
with a single pass of a document, processing each line independently.
|
||||
As per gopher, links can only be displayed one per line, encouraging
|
||||
neat, list-like structure.
|
||||
|
||||
Similar to how the two-digit Gemini status codes were designed so that
|
||||
simple clients can function correctly while ignoring the second digit,
|
||||
the text/gemini format has been designed so that simple clients can
|
||||
ignore the more advanced features and still remain very usable.
|
||||
|
||||
1.3.5.2 Line-orientation
|
||||
|
||||
As mentioned, the text/gemini format is line-oriented. Each line of a
|
||||
text/gemini document has a single "line type". It is possible to
|
||||
unambiguously determine a line's type purely by inspecting its first
|
||||
three characters. A line's type determines the manner in which it
|
||||
should be presented to the user. Any details of presentation or
|
||||
rendering associated with a particular line type are strictly limited
|
||||
in scope to that individual line.
|
||||
|
||||
There are 6 different line types in total. However, a fully
|
||||
functional and specification compliant Gemini client need only
|
||||
recognise and handle 4 of them - these are the "core line types",
|
||||
(see 1.3.5.3). Advanced clients can also handle the additional
|
||||
"advanced line types" (see 1.3.5.4). Simple clients can treat all
|
||||
advanced line types as one of the core line types and still offer an
|
||||
adequate user experience.
|
||||
|
||||
1.3.5.3 Core line types
|
||||
|
||||
The four core line types are:
|
||||
|
||||
1.3.5.3.1 Text lines
|
||||
|
||||
Text lines are the most fundamenal line type - any line which does
|
||||
not match the definition of another line type defined below defaults
|
||||
to being a text line. The majority of lines in a typical text/gemini
|
||||
document will be text lines.
|
||||
|
||||
Text lines should be presented to the user, after being wrapped to
|
||||
the appropriate width for the client's viewport (see below). Text
|
||||
lines may be presented to the user in a visually pleasing manner
|
||||
for general reading, the precise meaning of which is at the
|
||||
client's discretion. For example, variable width fonts may be used,
|
||||
spacing may be normalised, with spaces between sentences being made
|
||||
wider than spacing between words, and other such typographical
|
||||
niceties may be applied. Clients may permit users to customise the
|
||||
appearance of text lines by altering the font, font size, text and
|
||||
background colour, etc. Authors should not expect to exercise any
|
||||
control over the precise rendering of their text lines, only of their
|
||||
actual textual content. Content such as ASCII art, computer source
|
||||
code, etc. which may appear incorrectly when treated as such should
|
||||
be enclosed beween preformatting toggle lines (see 1.3.5.3.3).
|
||||
|
||||
Blank lines are instances of text lines and have no special meaning.
|
||||
They should be rendered individually as vertical blank space each
|
||||
time they occur. In this way they are analogous to <br/> tags in HTML.
|
||||
Consecutive blank lines should NOT be collapsed into a fewer blank
|
||||
lines. Note also that consecutive non-blank text lines do not form
|
||||
any kind of coherent unit or block such as a "paragraph": all text
|
||||
lines are independent entities.
|
||||
|
||||
Text lines which are longer than can fit on a client's display device
|
||||
SHOULD be "wrapped" to fit, i.e. long lines should be split (ideally
|
||||
at whitespace or at hyphens) into multiple consecutive lines of a
|
||||
device-appropriate width. This wrapping is applied to each line of
|
||||
text independently. Multiple consecutive lines which are shorter
|
||||
than the client's display device MUST NOT be combined into fewer,
|
||||
longer lines.
|
||||
|
||||
In order to take full advantage of this method of text formatting,
|
||||
authors of text/gemini content SHOULD avoid hard-wrapping to a
|
||||
pecific fixed width, in contrast to the convention in Gopherspace
|
||||
where text is typically wrapped at 80 characters or fewer. Instead,
|
||||
text which should be displayed as a contiguous block should be written
|
||||
as a single long line. Most text editors can be configured to
|
||||
"soft-wrap", i.e. to write this kind of file while displaying the long
|
||||
lines wrapped to fit the author's display device.
|
||||
|
||||
Authors who insist on hard-wrapping their content MUST be aware that
|
||||
the content will display neatly on clients whose display device is as
|
||||
wide as the hard-wrapped length or wider, but will appear with
|
||||
irregular line widths on narrower clients.
|
||||
|
||||
1.3.5.3.2 Link lines
|
||||
|
||||
Lines beginning with the two characters "=>" are link lines, which
|
||||
have the following syntax:
|
||||
|
||||
=>[<whitespace>]<URL>[<whitespace><USER-FRIENDLY LINK NAME>]<CR><LF>
|
||||
|
||||
where:
|
||||
|
||||
* <whitespace> is any non-zero number of consecutive spaces or
|
||||
tabs
|
||||
* Square brackets indicate that the enclosed content is
|
||||
optional.
|
||||
* <URL> is a URL, which may be absolute or relative. If the URL does
|
||||
not include a scheme, a scheme of gemini:// is implied.
|
||||
|
||||
All the following examples are valid link lines:
|
||||
|
||||
=> gemini://example.org/
|
||||
=> gemini://example.org/ An example link
|
||||
=> gemini://example.org/foo Another example link at the same host
|
||||
=>gemini://example.org/bar Yet another example link at the same host
|
||||
=> foo/bar/baz.txt A relative link
|
||||
=> gopher://example.org:70/1 A gopher link
|
||||
|
||||
Note that link URLs may have schemes other than gemini://. This means
|
||||
that Gemini documents can simply and elegantly link to documents
|
||||
hosted via other protocols, unlike gophermaps which can only link to
|
||||
non-gopher content via a non-standard adaptation of the `h` item-type.
|
||||
|
||||
Clients can present links to users in whatever fashion the client
|
||||
author wishes.
|
||||
|
||||
1.3.5.3.3 Preformatting toggle lines
|
||||
|
||||
Any line whose first three characters are "```" (i.e. three
|
||||
consecutive back ticks with no leading whitespace) are preformatted
|
||||
toggle lines. These lines should NOT be included in the rendered
|
||||
output shown to the user. Instead, these lines toggle the parser
|
||||
between preformatted mode being "on" or "off". Preformatted mode
|
||||
should be "off" at the beginning of a document. The current status
|
||||
of preformatted mode is the only internal state a parser is required
|
||||
to maintain. When preformatted mode is "on", the usual rules for
|
||||
identifying line types are suspended, and all lines should be
|
||||
identified as preformatted text lines (see 1.3.5.3.4).
|
||||
|
||||
Preformatting toggle lines can be thought of as analogous to <pre> and
|
||||
</pre> tags in HTML.
|
||||
|
||||
1.3.5.3.4 Preformatted text lines
|
||||
|
||||
Preformatted text lines should be presented to the user in a "neutral",
|
||||
monowidth font without any alteration to whitespace or stylistic
|
||||
enhancements. Graphical clients should use scrolling mechanisms to
|
||||
present preformatted text lines which are longer than the client
|
||||
viewport, in preference to wrapping. In displaying preformatted text
|
||||
lines, clients should keep in mind applications like ASCII art and
|
||||
computer source code: in particular, source code in langugaes with
|
||||
significant whitespace (e.g. Python) should be able to be copied and
|
||||
pasted from the client into a file and interpreted/compiled without
|
||||
any problems arising from the client's manner of displaying them.
|
||||
|
||||
1.3.5.4 Advanced line types
|
||||
|
||||
The following advanced line types MAY be recognised by advanced
|
||||
clients. Simple clients may treat them all as text lines as per
|
||||
1.3.5.3.1 without any loss of essential function.
|
||||
|
||||
1.3.5.4.1 Heading lines
|
||||
|
||||
Lines beginning with "#" are heading lines. Heading lines consist of
|
||||
one, two or three consecutive "#" characters, followed by optional
|
||||
whitespace, followed by heading text. The number of # characters
|
||||
indicates the "level" of header; #, ## and ### can be thought of as
|
||||
analogous to <h1>, <h2> and <h3> in HTML.
|
||||
|
||||
Heading text should be presented to the user, and clients MAY use
|
||||
special formatting, e.g. a larger or bold font, to indicate its
|
||||
status as a header (simple clients may simply print the line,
|
||||
including its leading #s, without any styling at all). However, the
|
||||
main motivation for the definition of heading lines is not stylistic
|
||||
but to provide a machine-readable representation of the internal
|
||||
structure of the document. Advanced clients can use this information
|
||||
to, e.g. display an automatically generated and hierarchically
|
||||
formatted "table of contents" for a long document in a side-pane,
|
||||
allowing users to easily jump to specific sections without excessive
|
||||
scrolling. CMS-style tools automatically generating menus or
|
||||
Atom/RSS feeds for a directory of text/gemini files can use first
|
||||
heading in the file as a human-friendly title.
|
||||
|
||||
1.3.5.4.2 Unordered list items
|
||||
|
||||
Lines beginning with a * are unordered list items. This line type
|
||||
exists purely for stylistic reasons. The * may be replaced in
|
||||
advanced clients by a bullet symbol. Any text after the * character
|
||||
should be presented to the user as if it were a text line, i.e.
|
||||
wrapped to fit the viewport and formatted "nicely". Advanced clients
|
||||
can take the space of the bullet symbol into account when wrapping
|
||||
long list items to ensure that all lines of text corresponding to
|
||||
the item are offset an equal distance from the left of the screen.
|
||||
|
||||
1.4 TLS
|
||||
|
||||
1.4.1 Version requirements
|
||||
|
||||
Use of TLS for Gemini transactions is mandatory.
|
||||
|
||||
Servers MUST use TLS version 1.2 or higher and SHOULD use TLS version
|
||||
1.3 or higher. Clients MAY refuse to connect to servers using TLS
|
||||
version 1.2 or lower.
|
||||
|
||||
1.4.2 Server certificate validation
|
||||
|
||||
Clients can validate TLS connections however they like (including not
|
||||
at all) but the strongly RECOMMENDED approach is to implement a
|
||||
lightweight "TOFU" certificate-pinning system which treats self-signed
|
||||
certificates as first- class citizens. This greatly reduces TLS
|
||||
overhead on the network (only one cert needs to be sent, not a whole
|
||||
chain) and lowers the barrier to entry for setting up a Gemini site
|
||||
(no need to pay a CA or setup a Let's Encrypt cron job, just make a
|
||||
cert and go).
|
||||
|
||||
TOFU stands for "Trust On First Use" and is public-key security model
|
||||
similar to that used by OpenSSH. The first time a Gemini client
|
||||
connects to a server, it accepts whatever certificate it is presented.
|
||||
That certificate's fingerprint and expiry date are saved in a
|
||||
persistent database (like the .known_hosts file for SSH), associated
|
||||
with the server's hostname. On all subsequent connections to that
|
||||
hostname, the received certificate's fingerprint is computed and
|
||||
compared to the one in the database. If the certificate is not the
|
||||
one previously received, but the previous certificate's expiry date
|
||||
has not passed, the user is shown a warning, analogous to the one web
|
||||
browser users are shown when receiving a certificate without a
|
||||
signature chain leading to a trusted CA.
|
||||
|
||||
This model is by no means perfect, but it is not awful and is vastly
|
||||
superior to just accepting self-signed certificates unconditionally.
|
||||
|
||||
1.4.3 Transient client certificate sessions
|
||||
|
||||
Self-signed client certificates can optionally be used by Gemini
|
||||
clients to permit servers to recognise subsequent requests from the
|
||||
same client as belonging to a single "session". This facilitates
|
||||
maintaining state in server-side applications. The functionality is
|
||||
very similar to HTTP cookies, but with important differences.
|
||||
|
||||
Whereas HTTP cookies are originally created by a webserver and given
|
||||
to a client via a response header, client certificates are created by
|
||||
the client and given to the server as part of the TLS handshake:
|
||||
Client certificates are fundamentally a client-centric means of
|
||||
identification. Further, whereas HTTP cookies can be "resurrected" by
|
||||
webservers after a client deletes them if the server recognises the
|
||||
client by means of browser finger-printing or some other tracking
|
||||
technology (leading to unkillable "super cookies"), if a client
|
||||
deletes a client certificate and also the accompanying private key
|
||||
(which the server has never seen), then the session ID can never be
|
||||
recreated. Thus clients not only need to opt in to a certificate
|
||||
session, but once they have done so they retain a guaranteed ability
|
||||
to opt out of it at any point and the server cannot defeat this
|
||||
ability.
|
||||
|
||||
Gemini requests typically will be made without a client certificate
|
||||
being sent to the server. If a requested resource is part of a
|
||||
server-side application which requires persistent state, a Gemini
|
||||
server can return a status code of 61 (see Appendix 1 below) to
|
||||
request that the client repeat the request with a "transient
|
||||
certificate" to initiate a client certificate section.
|
||||
|
||||
Interactive clients for human users MUST inform users that such a
|
||||
session has been requested and require the user to approve generation
|
||||
of such a certificate. Transient certificates MUST NOT be generated
|
||||
automatically.
|
||||
|
||||
Transient certificates are limited in scope to a particular domain.
|
||||
Transient certificates MUST NOT be reused across different domains.
|
||||
|
||||
Transient certificates MUST be permanently deleted when the matching
|
||||
server issues a response with a status code of 21 (see Appendix 1
|
||||
below).
|
||||
|
||||
Transient certificates MUST be permanently deleted when the client
|
||||
process terminates.
|
||||
|
||||
Transient certificates SHOULD be permanently deleted after not having
|
||||
been used for more than 24 hours.
|
||||
|
||||
Appendix 1. Full two digit status codes
|
||||
|
||||
10 INPUT
|
||||
|
||||
As per definition of single-digit code 1 in 1.3.2.
|
||||
|
||||
20 SUCCESS
|
||||
|
||||
As per definition of single-digit code 2 in 1.3.2.
|
||||
|
||||
21 SUCCESS - END OF CLIENT CERTIFICATE SESSION
|
||||
|
||||
The request was handled successfully and a response body will
|
||||
follow the response header. The <META> line is a MIME media
|
||||
type which applies to the response body. In addition, the
|
||||
server is signalling the end of a transient client certificate
|
||||
session which was previously initiated with a status 61
|
||||
response. The client should immediately and permanently
|
||||
delete the certificate and accompanying private key which was
|
||||
used in this request.
|
||||
|
||||
30 REDIRECT - TEMPORARY
|
||||
|
||||
As per definition of single-digit code 3 in 1.3.2.
|
||||
|
||||
31 REDIRECT - PERMANENT
|
||||
|
||||
The requested resource should be consistently requested from
|
||||
the new URL provided in future. Tools like search engine
|
||||
indexers or content aggregators should update their
|
||||
configurations to avoid requesting the old URL, and end-user
|
||||
clients may automatically update bookmarks, etc. Note that
|
||||
clients which only pay attention to the initial digit of
|
||||
status codes will treat this as a temporary redirect. They
|
||||
will still end up at the right place, they just won't be able
|
||||
to make use of the knowledge that this redirect is permanent,
|
||||
so they'll pay a small performance penalty by having to follow
|
||||
the redirect each time.
|
||||
|
||||
40 TEMPORARY FAILURE
|
||||
|
||||
As per definition of single-digit code 4 in 1.3.2.
|
||||
|
||||
41 SERVER UNAVAILABLE
|
||||
|
||||
The server is unavailable due to overload or maintenance.
|
||||
(cf HTTP 503)
|
||||
|
||||
42 CGI ERROR
|
||||
|
||||
A CGI process, or similar system for generating dynamic
|
||||
content, died unexpectedly or timed out.
|
||||
|
||||
43 PROXY ERROR
|
||||
|
||||
A proxy request failed because the server was unable to
|
||||
successfully complete a transaction with the remote host.
|
||||
(cf HTTP 502, 504)
|
||||
|
||||
44 SLOW DOWN
|
||||
|
||||
Rate limiting is in effect. <META> is an integer number of
|
||||
seconds which the client must wait before another request is
|
||||
made to this server.
|
||||
(cf HTTP 429)
|
||||
|
||||
50 PERMANENT FAILURE
|
||||
|
||||
As per definition of single-digit code 5 in 1.3.2.
|
||||
|
||||
51 NOT FOUND
|
||||
|
||||
The requested resource could not be found but may be available
|
||||
in the future.
|
||||
(cf HTTP 404)
|
||||
(struggling to remember this important status code? Easy:
|
||||
you can't find things hidden at Area 51!)
|
||||
|
||||
52 GONE
|
||||
|
||||
The resource requested is no longer available and will not be
|
||||
available again. Search engines and similar tools should
|
||||
remove this resource from their indices. Content aggregators
|
||||
should stop requesting the resource and convey to their human
|
||||
users that the subscribed resource is gone.
|
||||
(cf HTTP 410)
|
||||
|
||||
53 PROXY REQUEST REFUSED
|
||||
|
||||
The request was for a resource at a domain not served by the
|
||||
server and the server does not accept proxy requests.
|
||||
|
||||
59 BAD REQUEST
|
||||
|
||||
The server was unable to parse the client's request,
|
||||
presumably due to a malformed request.
|
||||
(cf HTTP 400)
|
||||
|
||||
60 CLIENT CERTIFICATE REQUIRED
|
||||
|
||||
As per definition of single-digit code 6 in 1.3.2.
|
||||
|
||||
61 TRANSIENT CERTIFICATE REQUESTED
|
||||
|
||||
The server is requesting the initiation of a transient client
|
||||
certificate session, as described in 1.4.3. The client should
|
||||
ask the user if they want to accept this and, if so, generate
|
||||
a disposable key/cert pair and re-request the resource using it.
|
||||
The key/cert pair should be destroyed when the client quits,
|
||||
or some reasonable time after it was last used (24 hours?
|
||||
Less?)
|
||||
|
||||
62 AUTHORISED CERTIFICATE REQUIRED
|
||||
|
||||
This resource is protected and a client certificate which the
|
||||
server accepts as valid must be used - a disposable key/cert
|
||||
generated on the fly in response to this status is not
|
||||
appropriate as the server will do something like compare the
|
||||
certificate fingerprint against a white-list of allowed
|
||||
certificates. The client should ask the user if they want to
|
||||
use a pre-existing certificate from a stored "key chain".
|
||||
|
||||
63 CERTIFICATE NOT ACCEPTED
|
||||
|
||||
The supplied client certificate is not valid for accessing the
|
||||
requested resource.
|
||||
|
||||
64 FUTURE CERTIFICATE REJECTED
|
||||
|
||||
The supplied client certificate was not accepted because its
|
||||
validity start date is in the future.
|
||||
|
||||
65 EXPIRED CERTIFICATE REJECTED
|
||||
|
||||
The supplied client certificate was not accepted because its
|
||||
expiry date has passed.
|
45
src/main.rs
Normal file
45
src/main.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype};
|
||||
use std::net::{TcpListener, TcpStream, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
mod response;
|
||||
|
||||
fn build_acceptor() -> std::sync::Arc<SslAcceptor> {
|
||||
let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap();
|
||||
acceptor.set_private_key_file("doc/key.pem", SslFiletype::PEM).unwrap();
|
||||
acceptor.set_certificate_chain_file("doc/cert.pem").unwrap();
|
||||
acceptor.check_private_key().unwrap();
|
||||
return Arc::new(acceptor.build());
|
||||
}
|
||||
|
||||
fn handle_client(mut stream: SslStream<TcpStream>) {
|
||||
let mut buffer = [0; 1026];
|
||||
stream.ssl_read(&mut buffer);
|
||||
let request = String::from_utf8(buffer.to_vec()).unwrap();
|
||||
|
||||
let header = response::Header::new(response::Status::Success, "text/gemini".to_string());
|
||||
let response = response::Response::new(header, [].to_vec());
|
||||
|
||||
stream.ssl_write(&response.format());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let addrs = [
|
||||
SocketAddr::from(([127, 0, 0, 1], 1965)),
|
||||
];
|
||||
let acceptor = build_acceptor();
|
||||
let listener = TcpListener::bind(&addrs[..]).unwrap();
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
let acceptor = acceptor.clone();
|
||||
thread::spawn(move || {
|
||||
let stream = acceptor.accept(stream).unwrap();
|
||||
handle_client(stream);
|
||||
});
|
||||
}
|
||||
Err(e) => { /* connection failed */ }
|
||||
}
|
||||
}
|
||||
}
|
50
src/response.rs
Normal file
50
src/response.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use std::vec;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Status {
|
||||
Input = 1,
|
||||
Success = 2,
|
||||
Redirect = 3,
|
||||
TemporaryFailure = 4,
|
||||
PermanentFailure = 5,
|
||||
ClientCertificateRequired = 6,
|
||||
}
|
||||
|
||||
pub struct Header {
|
||||
status: Status,
|
||||
meta: String,
|
||||
}
|
||||
|
||||
pub struct Response {
|
||||
header: Header,
|
||||
data: std::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(status: Status, meta: String) -> Header {
|
||||
return Header{
|
||||
status: status,
|
||||
meta: meta,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&self) -> String {
|
||||
let status: u8 = self.status as u8;
|
||||
return format!("{} {}\r\n", status * 10, self.meta)
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn new(header: Header, data: std::vec::Vec<u8>) -> Response {
|
||||
return Response{
|
||||
header: header,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&self) -> std::vec::Vec<u8> {
|
||||
let mut resp: std::vec::Vec<u8> = self.header.format().as_bytes().to_vec();
|
||||
resp.extend(&self.data);
|
||||
return resp;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue