notebook

The engineering notebook

How the machine at winsock.dev was built, what it is measured against, and the places where it was wrong first. The front page is the argument; this is the working.

A table you cannot paraphrase

The link automaton is not a model of RFC 1661. It is the state transition table printed in section 4.1 of RFC 1661, transcribed cell by cell: ten states across, sixteen events down, a hundred and sixty cells. Nothing in the page decides a transition. Every one is a lookup, so the page cannot be wrong about the protocol without a test going red.

The RFC's own description of the notation is two sentences, and both are load-bearing:

“State transitions and actions are represented in the form action/new-state.”

“The dash (‘-’) indicates an illegal transition.”

The dash is the part worth dwelling on. A hundred and sixty cells is not a hundred and sixty behaviours: a great many of them are the protocol saying this cannot happen, and a machine that silently did something reasonable in those cells would be a different protocol that mostly agreed. So an illegal transition is refused here and the refusal is printed, with the RFC's own sentence as the reason.

The table is generated into JavaScript from a JSON transcription by a script the deploy chain re-runs with --check, because a table typed twice is a table that will disagree with itself.

An MD5, written out, and checked three ways

CHAP's answer is a real hash. RFC 1994 says the response is “the one-way hash calculated over a stream of octets consisting of the Identifier, followed by (concatenated with) the ‘secret’, followed by (concatenated with) the Challenge Value”, sixteen octets for MD5. A page that showed sixteen made-up bytes there would be showing nothing at all, so this one computes it.

SubtleCrypto does not offer MD5 and is right not to, so it is written out. Which raises the obvious question: how do you know it is correct? Three ways, none of which is reading the code:

The constants are derived rather than typed. T[i] is floor(2^32 × abs(sin(i + 1))), which is what RFC 1321 section 3.4 says it is; sixty-four constants typed by hand is sixty-four chances to be wrong.

The same machine, one layer up

There is no second state machine on this page for IPCP, and that is not a shortcut. RFC 1332 says it in one line: “IPCP uses the same packet exchange machanism as the Link Control Protocol (LCP).” The typo is the RFC's, and quoting it means quoting it.

So the address negotiation feeds its packets into LCP's own table, and the test asserts that: the events IPCP produces are events LCP's table knows, and running them leaves the automaton where the protocol says it should be. That turns a sentence in a specification into something the build can check.

It also turned up a fact worth having. Running an address negotiation alone leaves the automaton in Ack-Rcvd, not Opened, because getting an address configures your end. The peer configures its own end over the same link, and only when both directions have been acknowledged is the protocol Opened. That is the same reason RFC 1661 has two Ack states rather than one. The test expected Opened, and the mechanism was right.

Transcribing a picture

TCP's eleven states come from figure 6 of RFC 793, which is a diagram drawn in ASCII in 1981. Transcribing a picture is not like transcribing a table: there is no grammar to check yourself against, and an arrow read off by eye can land on the wrong box without looking wrong.

So the transcription is checked in ways that survive a mis-drawn arrow. Every label on every arrow — rcv SYN,ACK, snd ACK, Timeout=2MSL — must appear verbatim in the archived figure. Every state description must be the RFC's own words. And two whole-table properties have to hold: every state reachable from CLOSED, and every state able to get back to it. Exchanging two arrows leaves the labels intact and breaks the reachability.

The diagram is also partial, and the RFC says so before anybody else can: it “illustrates only state changes, together with the causing events and resulting actions, but addresses neither error conditions nor actions which are not connected with state changes.” A state and event the figure does not join is therefore refused here rather than guessed at. The simultaneous close, where both ends send FIN at once, is not in figure 6 and is not on this page.

Counting what a crawler got wrong

The claim on the front page is that under HTTP/0.9 a program cannot tell a document from an error. That is easy to assert and easy to get slightly wrong, so the panel computes it instead: the same five pages are fetched under each version of the protocol, with the same five outcomes at the far end, and the table counts how many each version got right.

Under 1.0 the answer is all five, because there is a status line. Under 0.9 the only thing a client has is the body, so the crawler does what crawlers of the period did and looks for something that smells like an error. Two of the five come out wrong, and the interesting one is not the error page it stores. It is the real page it throws away, whose subject happens to be errors. A crawler that drops a page for containing the word is not leaving a gap you can see; it is leaving one nobody finds by looking.

Where this page was wrong first

Kept because a notebook that only records the things that worked is an advertisement.

What is not here

No timing, no retransmission, no windows, no MTU. No traffic at all once a connection is open: the subject is how each layer decided it was ready, not what it then carried. Authentication is PAP and CHAP and not the later variants. The challenges change every time, as RFC 1994 requires, but they come from a counter with a fixed seed rather than anything unpredictable, because a demonstration you cannot repeat is one nobody can check. And nothing here authenticates anything: MD5 has been unfit for that purpose since the 1990s.

Sources