1. 36
  1. 15

    Protocol decay is a real pain. If you need to write an HTTP/1.1 proxy that is compatible with everything thrown at browsers, you can write 99% of the parser in a day, and then spend a year chasing WTF edge cases and horribly broken servers that you still have to entertain, because their garbage happens to work in browsers that accumulated decades of workarounds.

    1. 14

      I have often said that Postel’s Law only really makes sense in a slow-moving world of software, where updates are performed by posting tapes around. These days we can and should demand stricter conformance. I’m glad someone has written this up as an internet draft.

      1. 13

        Reading this made me realize that if you have a network of machines communicating using a robustness principle, then you have no feedback loop on the “strict in what you send” part of the principle!

        So you just have a bunch of stuff being lax everywhere… pretty funny honestly

        1. 10

          Looking back at the original sources, it seems like the meaning of the Robustness Principle changed over time.

          Postel’s original quote, in the Internal Protocol spec in 1979: https://www.postel.org/ien/txt/ien111.txt

          The implementation of a protocol must be robust. Each implementation must expect to interoperate with others created by different individuals. While the goal of this specification is to be explicit about the protocol there is the possibility of differing interpretations. In general, an implementation should be conservative in its sending behavior, and liberal in its receiving behavior. That is, it should be careful to send well-formed datagrams, but should accept any datagram that it can interpret (e.g., not object to technical errors where the meaning is still clear).

          I read this as “by all means, throw errors if someone sends you something that unambiguously violates the spec. But where the spec is ambiguous (when there is the possibility of ‘differing interpretations’), then you should be liberal in what you accept”.

          Then later in 1989, “Requirements for Internet Hosts – Communication Layers” (RFC 1122): https://datatracker.ietf.org/doc/html/rfc1122

          “Be liberal in what you accept, and conservative in what you send”

          Software should be written to deal with every conceivable error, no matter how unlikely; sooner or later a packet will come in with that particular combination of errors and attributes, and unless the software is prepared, chaos can ensue. In general, it is best to assume that the network is filled with malevolent entities that will send in packets designed to have the worst possible effect. This assumption will lead to suitable protective design, although the most serious problems in the Internet have been caused by unenvisaged mechanisms triggered by low-probability events; mere human malice would never have taken so devious a course!

          Adaptability to change must be designed into all levels of Internet host software. As a simple example, consider a protocol specification that contains an enumeration of values for a particular header field – e.g., a type field, a port number, or an error code; this enumeration must be assumed to be incomplete. Thus, if a protocol specification defines four possible error codes, the software must not break when a fifth code shows up. An undefined code might be logged (see below), but it must not cause a failure.

          Note the examples. The network is filled with malevolent entities, so you should deal with “every conceivable error”. Presumably by handling the error appropriately without crashing, and not by ignoring it and moving on. And you must be prepared to deal with predictable change, like open enums. Again, this doesn’t read to me as saying that if someone unambiguously violates the spec (in a way that isn’t conceivably due to using a later version of the spec than you were built for) that you should not error.

          And finally, in “Architectural Principles of the Internet” in 1996 (RFC 1958): https://www.rfc-editor.org/rfc/rfc1958

          3.9 Be strict when sending and tolerant when receiving. Implementations must follow specifications precisely when sending to the network, and tolerate faulty input from the network. When in doubt, discard faulty input silently, without returning an error message unless this is required by the specification.

          Pretty unambiguously, “you should accept garbage”.

          1. 8

            I think the idea to either silently accept bad inputs or silently discard them is the worst part of Postel’s law. It makes interactions with misbehaving software especially hard to debug for both sides.

            I believe that protocol violation detections should be as loud as possible, especially for application layer protocols. Software should have ways to tell its users “you are talking to an incorrect implementation, let its operator know about it” or “your peer uses an outdated protocol, let them know they should upgrade”.

            1. 4

              There’s also a big security question to it. Pretty much every security exploit begins with a slightly malformed input. Validating and rejecting malformed inputs eliminates a large class of attacks.

              1. 2

                https://en.wikipedia.org/wiki/Offensive_programming

                (Only tolerate expectable errors from input; only handle expectable errors internally.)

              2. 4

                This ties into the signal post on federated systems being unable to change.

                I wonder if these ideas are independent, or if compromises in one could improve the other

                1. 9

                  Yeah, I think the thing to realize is there’s a spectrum between “don’t bother specifying a protocol, just write an app and don’t permit other implementations” and “Write a document, throw it over the wall, never touch it again and bend over backward to interoperate with other implementations without talking to each other. The things that make it possible for small teams working on one app move so quickly are:

                  1. Communication
                  2. Limited duplication of effort

                  (1) Is something I think we could get a lot better at, both culturally in terms of getting in the habit of actually talking to people in other projects, and also in terms of workflow: we have dramatically better tooling for this kind of thing for code than we used to; distributed version control post-dates even xmpp, let alone older protocols. With the exception of BitKeeper, the gap is several years wide. We should be using these for specifications in the same way we do for code, with tags & branches, without exception.

                  Right now I’m in the midst of trying to push an incredibly minor tweak to webrtc through the w3c, and rather than having a branch for revisions to the main spec, there is an entirely separate document called webrtc-extensions, that is essentially a series of patches to the main document, written in English instead of as an actual diff. This is a very silly workflow.

                  Stuff like protobuf/capnproto gets rid of a lot of the grunt work both for specifying low level protocol details and for actually implementing protocols, but even a lot of newer stuff like matrix is just json with a prose description of the higher-level semantics. We switched from purely English prose to formal grammars for syntax specification decades ago, and we should use more formal/well-specified/tool-friendly descriptions elsewhere too. Indeed, one of the things that is actually nice about the way web standards are written is that the javascript APIs use something called WebIDL, rather than having ad-hoc English descriptions of the signatures of methods etc.

                  Some of that also bleeds into (2), since it means not having to write your own serialization code/api bindings etc. Actually, this is starting to shift into the realm of sharing code rather than having a separately “documented” protocol. But maybe we should do more of that? It’s not like the semantics of protobuf aren’t themselves documented.

                  …wading into the yet-to-be-built, it’s worth taking this a bit further I think: why restrict ourselves to type declarations? Again with the w3c specs, there are algorithms described, which use a very particular stylized form of English – with even some limited linter support. Could we just make this a library in some suitable DSL, rather than having people have to divine a formal description and translate it into some programming language by hand? Why is a spec for an algorithm different from a grammar, and can we explore the design of programming languages that are simple enough and self-contained enough to just compile to $your_language for this kind of thing? (I think better interoperability between languages is a big open problem that is worth exploration).