Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Note
This section contains information on how all crates in this repo work at the network level, allowing for recreation and modification without the requirement for understanding the Rust code included. This section is recommended for technologically advanced users.
Caution
Where this section says 'packet', it is NOT referring to a standard TCP packet. While this repo does use TCP, it uses a special header value unique to all DitherDude networking projects. See How to Write a Packet before blindly attempting to implement the below functionality.
Additionally, where this section mentions usage of status/error codes, this refers to a custom implementation of HTTP status codes. This means status codes used may have a slightly different implication to standard HTTP status codes, or include non-standard status codes. See Status Codes for a comprehensive list of how to map status codes to u32 byte chunks, and Sending Error Codes when a fatal error is to be reported to the client.
Server
Warning
This is not the guide for how to host a website, but rather how to construct the server framework. See Hosting a Website (will link) if you wish to build a website instead.
On Connection
The client will initiate a connection with the server with a data packet of size 14 bytes or greater. If the data is too short, the server responds with a TOO_SMALL error code. The data received is to be chunked accordingly:
| Bytes start | Bytes stop | Data type | Usage |
|---|---|---|---|
0 |
3 |
u32 |
Client version major (e.g. the 3 in 3.7.2) |
4 |
7 |
u32 |
Client version minor (e.g. the 7 in 3.7.2) |
8 |
11 |
u32 |
Client version patch ("tiny") (e.g. the 2 in 3.7.2) |
This version is then compared against server's version. See Compatibility to determinine if either:
- The client's version is too new for
serverand is not compatible => return error codeDOWNGRADE_REQUIRED. - The client's version it too old for
serverand is not compatible => return error codeUPGRADE_REQUIRED.
data is now set to bytes 12 and onward.
Note
As of version 0.1.1, the server checks if the data is empty here. I have no idea why I implemented this, as we confirmed earlier that the data is at least
13b, meaning that there would be at least1bof data after trimming, and therefore this check will never run.
| Bytes start | Bytes stop | Data type | Usage |
|---|---|---|---|
0 |
{x ∈ Z⁺ ∣ x≡0 (mod 5)} |
UTF-8 |
Stacks supported by client. Grouped as char sets of 5b, and terminated by a / byte. |
x+1 |
x+1 |
Byte => 0x2F |
Terminator of stacks field. Indicates the start of the request location. |
x+2 |
EOF |
String |
The location which the user requests to access. |
The following is looped until the 0x2F character is reached:
serverchecks if data length is less than 5. If so, return errorTOO_SMALL- Five bytes are popped off the front of data, and appended to an array of
[u8; 5](array of unknown quantity of array of typeBytegrouped in sets of 5). - If data is empty, return error
UNPROCESSABLE. - If
data[0](the firstByteof data) is0x2F(the/character), exit the loop.
Tip
As there are 256
UTF-8characters, and the only limitation is that a stack name cannot start with a/character, this means that there is a total of255*(256^4)=1,095,216,660,480possible stacks that could be implemented!
A sample packet could look like this:
\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x4D\x52\x4B\x44\x4E\x48\x54\x4D\x4C\x58\x2F\x70\x61\x67\x65
which translates to:
- Client is using version
0.1.1. - Client supports protocols
MRKDNandHTMLX. AsMRKDNis beforeHTMLX,MRKDNis preferred overHTMLX. - Client would like to access endpoint
/page.
server then loops through each client protocol, first to last. If the server supports the stack specified by the client (see Creating a stacks.txt File), the stack is selected, and the loop is exited.
If no stack could be selected, return error UNPROCESSABLE.
Creating a stacks.txt File
This is the file used by server to define how stacks are to be parsed. There are three different types of stack protocols: Relative, Definite, and Wildcard. The following example showcases the usage of all three types of protcols, and will be used for the explanation of each protocol below. For this example, assume your website is hosted on /path/to/website.
user@linux:/path/to/website$ cat stacks.txt
MRKDN index.md
HTMLX /index.htmx
RWDTA
user@linux:/path/to/website$
Tip
While I have a space between the stacks and the protocol, this is not needed, as the first
5brepresent the stack, and the protocol is whitespace trimmed. This means that the first line could also readMRKDNindex.mdorMRKDN index.md.
Relative protocol
MRKDN index.md
This is the most common protocol. This indicates that the following should be appended to the end of the request; i.e.
- Client requests endpoint
/=> file at/path/to/website/index.mdis returned. - Client requests endpoint
/page=> file at/path/to/website/page/index.mdis returned. - Client requests endpoint
////////account/login//////=> file at/path/to/website/account/login/index.mdis returned.
If the requested file does not exist, server returns error NOT_FOUND.
If the requested file exists but server cannot access it (e.g. missing required permissions), returns error NOT_FOUND. I don't know why it does this, in a future commit I may change this to error code FORBIDDEN.
Definite protocol
HTMLX /index.htmx
This protocol will always point to the same file, regardless of the endpoint; i.e.
- Client requests endpoint
/=> file at/path/to/website/index.htmxis returned. - Client requests endpoint
/page=> file at/path/to/website/index.htmxis returned. - Client requests endpoint
////////account/login//////=> file at/path/to/website/index.htmxis returned.
If the file at /path/to/website/index.htmx doesn't exist, error code SHAT_THE_BED is returned, as this is the result of a server-side misconfiguration.
Wildcard protocol
RWDTA
This protocol points to the file specified by the user's endpoint; i.e.
- Client requests endpoint
/=> return errorNOT_FOUND(as no file can exist with no name). - Client requests endpoint
/page=> file at/path/to/website/pageis returned. - Client requests endpoint
/forbidden/logs/credit_cards.csv=> file at/path/to/website/forbidden/logs/credit_cards.csvis returned.
Note
The Wildcard protocol uses the exact same method as the Define protocol; i.e. the returned file is
{endpoint}/{protocol}. This means that technically requesting Wildcard/pagewill request/page/. However, thepathcheck()function (which determines if the path is safe to access; e.g./../../passwords.txtshould in theory point to/path/passwords.txt, but this is caught and blocked) will trim the excess/at the end when creating the path buffer, returning file/path/to/website/page. If the requested file doesn't exist, error codeNOT_FOUNDis returned.
Returning the File
If a file is to be returned to the client, the return packet is structured as so:
| Bytes start | Bytes stop | Data type | Usage |
|---|---|---|---|
0 |
3 |
Byte => 0xC8000000 |
Success status code |
4 |
8 |
String |
Stack server used. As server might not use the first stack the user requested, server reports to the client which stack was used |
9 |
EOF |
String |
Content of file to be returned to the client. |
A sample response could look like this:
\xC8\x00\x00\x00\x4D\x52\x4B\x44\x4E\x23\x20\x48\x65\x6C\x6C\x6F\x2C\x20\x57\x6F\x72\x6C\x64\x21
which translates to:
serverresponds to request with staus codeSUCCESS.serverresolved to stackMRKDN.serversent file payload of# Hello, World!to client.
The connection to client is now dropped silently, and the connection handshake is over.
DNS Provider
Caution
The code of version
0.1.1is completely fucked up. While this will hopefully be fixed in a future commit, for now this wiki preempt each erroneous section of the API with a CAUTION box like this.
On Connection
The client will initiate a connection with the server with a data packet of size 14 bytes or greater. If the data is too short, the server responds with a TOO_SMALL error code. The data received is to be chunked accordingly:
| Bytes start | Bytes stop | Data type | Usage |
|---|---|---|---|
0 |
3 |
u32 |
Client version major (e.g. the 3 in 3.7.2) |
4 |
7 |
u32 |
Client version minor (e.g. the 7 in 3.7.2) |
8 |
11 |
u32 |
Client version patch ("tiny") (e.g. the 2 in 3.7.2) |
This version is then compared against server's version. See Compatibility to determinine if either:
- The client's version is too new for
serverand is not compatible => return error codeDOWNGRADE_REQUIRED. - The client's version it too old for
serverand is not compatible => return error codeUPGRADE_REQUIRED.
Caution
is_last_blockis likely supposed to be byte12. I will confirm this when I write the Backend (will link) wiki page.
data is set to bytes 13 and onward, and is_last_block (Bool) is set to byte 13
| Bytes start | Bytes stop | Data type | Usage |
|---|---|---|---|
12 |
12 |
Garbage |
{UNUSED}. Likely supposed to be the value for is_last_block. |
13 |
13 |
Bool |
is_last_block. Note how this is also the first byte of data. |
13 |
EOF |
String |
data; The block in a DNS chain that the user is requesting. |
is_last_block: Bool
Note
For this example, assume the user requests to resolve
my.site.com. To prevent confusion with future websites, I will use local192.168.0.XXXIPs as the "resolved" addresses.
When resolving a URL, dns_provider needs to know if the data you are requesting is the last block in the chain of URL blocks. Using the above example, if we query the dns_provider for all of com's children for site, we want it to return the address of where we should query the block my - and not return the location of the webpage for site.com. The is_last_block flag ensures that we return the specific record fields for our query type.
A sample packet could look like this:
\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\xFF\x73\x69\x74\x65
which translates to:
- Client is using version
0.1.1. - Client has the
is_last_blockbool set to 0x73. - Client is requesting the block "
site".
Response
Note
There are a multitude of responses that
dns_providercan return toclient. These will all be displayed concisely as a table at the end of this section. From this point on, the database that holds the data related to URL blocks is assumed to have at least the following columns:name,domain_ip,domain_port,dns_ip, anddns_port. There is no reason thatdomain_ip+domain_port, anddns_ip+dns_portshould be separate (i.e.name,domain, anddnsshould be sufficient). However, as this is how the system was initially set up, this is what will be followed in the below documentation.
Warning
This code uses
is_some()andunwrap()syntax instead ofif let Some(...). This will likely be corrected in a future commit.
The Wildcard Record
This special record is denoted by the . value, as it is a value that can be guaranteed won't collide with any valid records in the database. Though this record has a second use, only the first will be covered for now: The dns record.
In the event that this dns_provider is to move locations, dns_provider needs a way to inform client that all future requests should be sent to the new dns_provider. If the . record exists AND has dns_ip and dns_port values, dns_provider notifies client with error code PERMANENT_REDIRECT that it (dns_provider) is obsolete, and that the new (specified) dns_provider should be used instead. The new location is sent in the format {dns_ip}:{dns_port}.
A sample PERMANENT_REDIRECT response could look like this:
\x2D\x01\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x32\x3A\x36\x32\x30\x32
which translates to:
dns_providerhas moved to192.168.0.2port6202. All future DNS queries intended for thisdns_providershould be instead forwarded to192.168.0.2:6202.
Requested Block Record
If the . record is non-existent, or doesn't have dns_ip and dns_port values, the database is re-queried for client's specified block's domain_ip, domain_port, dns_ip, and dns_port fields. In this example, this would be the my block of the site dns_provider, OR the site block of the com dns_provider. If the record does not exist, see Resolve Wildcard. is_last_block is now checked.
Caution
The
is_last_blockimplementation of status codes is a literal hellhole. I will have to investigate if the below implementation ofGONEis correct OR if it should align with the one specified in Universal Information, and update the code. It should be assumed, however, that the implementation specified in Universal Information is correct, and that thedns_providercode below is corrupted.
is_last_block (True)
If the record has domain_ip and domain_port, the record is returned to client in the format {domain_ip}:{domain_port} with status code SUCCESS. E.g.
\xC8\x00\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x33\x3A\x36\x32\x30\x34
which translates to
- Block (which was the last block of a DNS block-chain) points to the
domainat192.168.0.3port6204.
If the record does not have a domain_ip or domain_port, (i.e. these values are NULL), dns_ip and dns_port are read. If they exist, they are returned to client with status code FOUND in the format {dns_ip}:{dns_port}. E.g.
\x2E\x01\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x33\x3A\x36\x32\x30\x32
which translates to
- Status code
FOUND. dns_provideracknowledges thatclientrequested this record asis_last_block. However, the record held aDNSvalue instead of the requesteddomainvalue. Hence, while the record was found, there is nodomainat the requested location. TheDNSvalue in the record points to192.168.0.3port6202.
If the record is missing all four fields (i.e. they are all NULL, but the record does exist), the status code GONE is sent to client.
Tip
Creating a record like so is usually for reserving blocks - i.e. you hint to
clientthat while no block exists here yet, one will likely be here soon, or that this block may not be requested for personal ownership, as it is in use. However, this is not the recommended implementation for parking blocks.
A sample response could be:
\x9A\x01\x00\x00
which translates to
dns_providerhas found the requested record, but there was no data associated with it.
!is_last_block (False)
Caution
Status codes
SUCCESSandFOUNDbelow should likely be swapped.
If the record has dns_ip and dns_port, the record is returned to client in the format {dns_ip}:{dns_port} with status code FOUND. E.g.
\x2E\x01\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x33\x3A\x36\x32\x30\x32
which translates to
- Intermediary block points to the
dns_providerat192.168.0.3port6202.
If the record does not have a dns_ip or dns_port, (i.e. these values are NULL), domain_ip and domain_port are read. If they exist, they are returned to client with status code SUCCESS in the format {domain_ip}:{domain_port}. E.g.
\xC8\x00\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x33\x3A\x36\x32\x30\x34
which translates to
- Status code
SUCCESS dns_provideracknowledges thatclientrequested this record as an intermediary block - i.e. as aDNS. However, the record held adomainvalue instead of the requestedDNSvalue. Hence, while the record was found, there is nodns_providerat the specified location. Thedomainvalue in the record points to192.168.0.3port6204.
If the record is missing all four fields (i.e. they are all NULL, but the record does exist), see Resolve Wildcard.
Caution
This should return the status code
GONElike it does foris_last_block(True).
Resolve Wildcard
If the record cannot be found, the . record is again queried - but this time for the fields domain_ip and domain_port. The location these fields point to indicate where the next sister dns_provider is located, if at all. Think of this as a kind of webring: This record ties in numerous dns_providers into one long chain. If the current dns_provider exhausts its records, it returns to the client a location where the next dns_provider is, and the query can be resumed there. Think of an example where there is provider1 which holds records starting with a-m, and provider2 which holds records starting with n-z. If client requests website from provider1, provider1 will exhaust its database, as it does not hold records starting with the letter w, and thus tells client where the sister provider2 is. client will then repeat the request for website to provider2, who has the record. provider2 will return the result as shown above. This Wildcard record is returned to client with status code NON_AUTHORITATIVE in the format {domain_ip}:{domain_port}. E.g.
\xCB\x00\x00\x00\x31\x39\x32\x2E\x31\x36\x38\x2E\x30\x2E\x34\x3A\x36\x32\x30\x32
which translates to
dns_providerhas exhausted its database, and couldn't find the specifed record. However,clientshould be aware thatdns_providerlocated at192.168.0.4port6202is the nextdns_providerin the chain, and should be queried for the requested record.
Warning
It is possible that the Wildcard chain is cyclic, i.e.
provider1points toprovider2which points toprovider1. In theclientimplementation in this repo, this can cause a Stack Overflow (which requires ~130NON_AUTHORITATIVEredirects). It is therefore the duty ofclientto keep a record of all prior Wildcard locations, and break the chain with a local error code ofLOOP_DETECTEDif adns_provideris to appear again.
Fallthrough
If the specified record doesn't exist, and the . record does not exist or doesn't contain the domain_ip and domain_port values, dns_provider returns the error code MISDIRECTED, and drops the connection.
Warning
MISDIRECTEDis likely an unsuitable error code to be returned.MISDIRCTEDindicates that the request was not of a valid recognisable format thatdns_providercould interpret. However,dns_providerwas able to interpret the request, except that it could not resolve the request.
A sample response could look like:
\xA5\x01\x00\x00
which translates to
dns_providerhas not moved.dns_providercould not locate the requested block in theDNSchain.dns_providerdid not have a pointer to a sisterdns_providerto resume the request.
Ending the Connection
Once data has been sent to client, dns_provider is expected to silently drop the connection to client, as the handshake is now over.
List of Possible Responses
The following table lists all the possible responses of dns_provider to a query. It should be followed in order from top-to-bottom. It is not intended to replace the above information, but instead preview it for quick reference.
is_last_block |
STATUS CODE |
Data | Purpose |
|---|---|---|---|
| - | PERMANENT_REDIRECT |
{dns_ip}:{port} |
The location of the new dns_provider if this current one is to become obsolete. |
True |
SUCCESS |
{domain_ip}:{domain_port} |
The location of the requested domain. |
True |
FOUND |
{dns_ip}:{port} |
The location of the DNS field of the requested record. |
True |
GONE |
- | There is no location associated with the requested record; however the record does exist. |
False |
FOUND |
{dns_ip}:{port} |
The location of the requested DNS. |
False |
SUCCESS` | {domain_ip}:{domain_port} |
The location of the domain field of the requested record. |
| - | NON_AUTHORITATIVE |
{domain_ip}:{domain_port} |
The location of the sister dns_provider. |
| - | MISDIRECTED |
- | Exhausted all attempts to locate the requested record. |