Este documento es una traducción desde el inglés original “How to Read an RFC” escrito por Mark Nottingham https://www.mnot.net/blog/2018/07/31/read_rfc (Tuesday, 31 July 2018). Traducción por Hugo Salgado, con permiso del autor.
Para bien o para mal, los documentos “Requests for Comments” (RFCs) son la forma como especificamos muchos protocolos en Internet. Estos documentos son tratados alternativamente o como textos sagrados por los desarrolladores que los analizan buscando significados ocultos, o luego descartados por irrelevantes cuando no se logran entender. Esto lleva a menudo a frustraciones y -más significativamente- problemas de interoperabilidad y seguridad.
Sin embargo, conociendo un poco los detalles internos de cómo son construidos y publicados, es un poco más fácil entender qué es lo que está escrito. Este es mi intento informado por mis experiencias con HTTP y algunas otras cosas.
El lugar oficial para encontrar RFCs es el Sitio Web del Editor de RFC (“RFC Editor”). Sin embargo, tal como veremos más adelante, ahí falta alguna información muy importante, así que la mayoría de la gente usa tools.ietf.org.
Incluso encontrar el RFC correcto puede ser difícil, debido a que hay tantos (actualmente, ¡casi 9.000!). Obviamente se pueden buscar con un motor de búsqueda genérico, y el “RFC Editor” tiene una sección de búsqueda excelente en su sitio.
Otra opción es rfc.fyi, que contruí para permitir búsqueda de RFCs por título y palabras claves, y exploración por marcadores (“tags”).
No es ningún secreto que los RFCs en texto plano son difíciles de leer, bordeando lo feo, pero las cosas están a punto de mejorar: el “RFC Editor” está trabajando en un nuevo formato para RFC, con una presentación mucho más agradable y opciones para personalización. Mientras tanto, si quieres RFCs más usables, se pueden utilizar repositorios hechos por terceros algunas selecciones; por ejemplo, greenbytes mantiene una lista de RFCs relacionados con WebDAV, y el Grupo de Trabajo HTTP mantiene una selección de los relacionados con HTTP.
Todos los RFCs tienen un encabezado en la parte superior que se ve algo así:
Internet Engineering Task Force (IETF) R. Fielding, Ed.
Request for Comments: 7230 Adobe
Obsoletes: 2145, 2616 J. Reschke, Ed.
Updates: 2817, 2818 greenbytes
Category: Standards Track June 2014
ISSN: 2070-1721
Arriba a la izquierda, este dice “Internet Engineering Task Force (IETF)”. Eso indica que es un producto de la IETF; aunque no es muy conocido, hay otras formas de publicar un RFC que no requiere consenso en IETF; por ejemplo, la rama independiente (“independent stream”).
De hecho, existen varias “ramas” (“streams”) donde se puede publicar un documento. Sólo la rama “IETF” indica que esa especifición de protocolo ha sido revisada por la IETF completa, y ha declarado su consenso.
Los documentos antiguos (más o menos antes del RFC5705) dicen “Network Working Group” ahí, así que debes investigar un poco más para averiguar si representó un consenso IETF; por ejemplo revisando la sección “Status of this Memo”, o el sitio del RFC Editor.
Por debajo viene el número de “Request for Comments”. Si en cambio dice “Internet-Draft”, entonces no es un RFC; sino solamente una propuesta, y cualquiera puede escribir una. Solo porque algo es un “Internet-Draft” no quiere decir que alguna vez será adoptado por la IETF.
El campo “Category” puede ser “Standards Track” , “Informational”, “Experimental”, o “Best Current Practice”. Las distinciones entre ellas son un poco borrosas, pero si es producida por la IETF (ver más arriba), ha tenido una cantidad razonable de revisión. Sin embargo, hay que destacar que Informational y Experimental no son estándares, incluso si es que hubo consenso de IETF para publicarse.
Finalmente, los autores del documento son listados en la parte derecha del encabezado. Al contrario de la academia, no es una lista completa de todos los que contribuyeron en el document; más bien eso se incluye al final del documento, en la sección de reconocimientos (“Acknowledgments”). En los RFCs, los autores son literalmente “quienes escribieron el documento”. A veces pueden tener el sufijo “Ed.”, lo que indica que actuaron como editores, generalmente porque el texto ya existía previamente (como cuando un RFC es revisado).
Los RFCs son una serie de documentos archivados; no pueden cambiar, incluso por una letra (ver las diferencias entre el RFC7158 y el RFC7159 como un ejemplo llevado al extremo; tenía un error en el año ;) ).
Resultado de esto, es importante saber que están mirando el documento correcto. El encabezado contiene un par de metadatos que pueden ayudar con esto:
“Obsoletes” (“hace obsoleto”): lista los RFCs que este documento reemplaza por completo. Es decir, deberías estar usando este documento, no los demás. Hay que tener en cuenta que una versión antigua de un protocolo no es necesariamente hecha obsoleta cuando sale una nueva; por ejemplo, HTTP/2 no hace obsoleto a HTTP/1.1, porque todavía es legítimo (y necesario) implementar el protocolo antiguo. Sin embargo, el RFC7230 sí hace obsoleto el RFC2616, porque es la referencia para ese protocolo.
“Updates” (“actualiza”): lista los RFCs a los que este documento hace cambios sustantivos; en otras palabras, si estás leyendo esos otros documentos, probablemente deberías leer este también.
Desafortunadamente, los RFCs en texto ASCII (por ejemplo los que están en el sitio “RFC Editor”) no dicen qué documentos actualizan o hacen obsoletos el que estás mirando actualmente. Esta es una razón por la que la mayoría de la gente usa el repositorio de RFCs en tools.ietf.org, que pone esta información en un encabezado como este:
“` [Docs] [txt|pdf] [draft-ietf-http…] [Tracker] [Diff1] [Diff2] [Errata]
Obsoleted by: 7230, 7231, 7232, 7233, 7234, 7235 DRAFT STANDARD Updated by: 2817, 5785, 6266, 6585 Errata Exist “`
Cada uno de los números en la página es un enlace, así que puedes accceder fácilmente a cada uno de ellos.
Incluso los RFC más actuales tienen problemas. En el encabezado del sitio “tools”, también se pueden ver unas advertencias en la derecha que dicen “Errata Exist”, con un enlace a la hoja de erratas correspondiente.
Las Erratas son correcciones y clarificaciones al documento que no son lo suficientemente importantes como para publicar un nuevo RFC. Sin embargo, a veces tienen un impacto sustancial en cómo un RFC es implementado (por ejemplo, si un bug en la especificación lleva a una mala interpretación importante), así que vale la pena revisarlas.
Por ejemplo, esta es la errata del RFC7230. Cuando se lee una errata hay que fijarse muy bien en su status; muchas son rechazadas porque alguien malinterpretó lo que leyó.
Es más común de lo que uno puede pensar que un desarrollador lea una declaración en un RFC, implemente lo que vió, y hacer lo contrario de lo que los autores esperaban.
Esto es porque es extremadamente difícil escribir una especificación de una manera que no pueda ser malinterpretada cuando se lea selectivamente (como es el caso con cualquier texto sagrado).
Como resultado de esto, es necesario leer no solo el texto relevate sino también, como mínimo, todo lo que referencia, sea en la misma especificación o en una distinta. En un apuro leer las secciones relacionadas ayudará mucho, si no es posible leer los documentos completos.
Por ejemplo, los encabezados de los mensajes HTTP están definidos como separados por CRLF, pero si te saltas a leer aquí abajo, verás que “un receptor PUEDE reconocer un LF único como terminador de línea, e ignorar cualquier CR que lo preceda”. Obvio, ¿cierto?
También es importante tener en cuenta que muchos protocolos crean registros en IANA para administrar sus puntos de extensión. Estos registros, y no las especificaciones, son las fuentes de la verdad. Por ejemplo, la lista canónica de métodos HTTP está en este registro, y no en ninguna de las especificaciones de HTTP.
Casi todos los RFCs tienen unas cláusulas repetitivas que se ven así, cerca del comienzo del documento:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
Estas palabras clave del RFC2119 ayudan a definir la interoperabilidad, pero muchas veces confunden a los desarrolladores. Es muy común ver una especificación que dice algo como:
"The Foo message MUST NOT contain a Bar header." (El mensaje Foo
NO DEBE contener un encabezado Bar)
Este requisito se instala sobre un artefacto del protocolo, el “mensaje Foo”. Si estás enviando uno de ellos, es bastante claro que es necesario no incluir un encabezado Bar. Si incluyes uno, no será un mensaje conforme al estándar.
Sin embargo, el comportamiento del receptor es mucho menos claro. Si te llega un mensaje Foo con un encabezado Bar, ¿qué haces?.
Algunos desarrolladores rechazarán el mensaje, aunque la especificación no dice nada al respecto. Otros procesarán el mensaje, pero eliminarán el encabezado Bar, o lo ignorarán – aún cuando la especificación dice explícitamente que todos los encabezados deben procesarse.
Todas estas cosas pueden -sin intención- causar problemas de interoperabilidad. La forma correcta de hacerlo es seguir con el procesamiento normal del encabezado, a menos que exista un requerimiento específico al contrario.
Esto es porque en general, las especificaciones están escritas de tal manera que los comportamientos están abiertamente especificados. En otras palabras, todo lo que no está específicamente prohibido, está permitido. Por lo tanto, buscar demasiado en la lectura de las especificaciones puede causar daño involuntariamente, ya que estarás introduciendo nuevos comportamientos que otros tendrán que sortear.
En un mundo ideal, la especificación debería ser definida en términos del comportamiento de los que manejarán el mensaje, como esto:
"Senders of the Foo message MUST NOT include a Bar header. Recipients
of a Foo message that includes a Bar header MUST ignore the Bar header,
but MUST NOT remove it." (Los transmisores del mensaje Foo NO DEBEN
incluir un encabezado Bar. Los receptores de un mensaje Foo que incluya
un encabezado Bar DEBEN ignorar el encabezado Bar, pero NO DEBEN
eliminarlo).
En ausencia de esto, es mejor buscar consejos generales de cómo manejar
los errores en otro lugar de la especificación (por ejemplo, la sección
Conformidad y Manejo de Errores
de HTTP).
También hay que tener en cuenta el destino de los requisitos; la mayoría de las especificaciones tienen un conjunto de términos altamente especializados que usan para distinguir entre los distintos roles de un protocolo.
Por ejemplo, HTTP tiene proxies, que son un tipo de intermediario que implementa tanto un cliente como un servidor (pero no un User-Agent o un servidor de origen); en este caso necesitan poner atención a los requisitos dirigidos a todos estos roles.
De igual manera, HTTP distingue entre “generar” un mensaje y meramente “reenviarlo” en alguno de sus requisitos, dependiendo de la situación específica. Poner atención a este tipo de terminología específica puede salvarte de un montón de suposiciones.
Sí, la palabra “SHOULD” (debería), merece su propia sección. Este término insípido infecta muchos RFCs, pese a todos los esfuerzos de erradicarlo. El RFC2119 lo describe como:
SHOULD Esta palabra, o el adjetivo "RECOMMENDED" (recomendado), significa
que pueden existir razones válidas en circunstancias particulares
para ignorar un ítem en particular, pero deben entenderse
completamente y valorarse cuidadosamente sus implicancias antes de
elegir un curso distinto.
En la práctica, los autores normalmente usan SHOULD y SHOULD NOT queriendo decir “Nos encantaría que lo hicieras así, pero sabemos que no podemos pedirlo siempre.”
Por ejemplo, en la introducción a los métodos HTTP, vemos:
Cuando se recibe un método de solicitud que no es reconocido o
no es implementado por un servidor de origen, el servidor de origen
DEBERÍA responder con el código de estado 501 (No Implementado).
Cuando se recibe un método de solicitud que es conocido por un
servidor de origen pero no está permitido para el recurso apuntado,
el servidor de origen DEBERÍA responder con el código de estado 405
(Método No Permitido).
Estos DEBERÍA no son DEBE porque el servidor puede razonablemente decidir tomar otra acción. Si la solicitud es de un cliente que se sospecha que es un atacante, puede cortar la conexión, o si se necesita autenticación HTTP para el recurso, puede obligar a hacerlo cumplir con un 401 (No Autenticado) antes que enviar un 405.
DEBERÍA no quiere decir que un servidor es libre de ignorar un requisito solo porque no sienta que no debe seguirlo.
A veces vemos un DEBERÍA que sigue esta forma:
Un transmisor que genera un mensaje que contiene un cuerpo con carga
útil DEBERÍA generar un campo de encabezado Content-Type en ese mensaje
a menos que el tipo de medio considerado para la representación
incluída sea desconocida para el transmisor.
Fíjense en el “a menos que” – está especificando las “circunstancias particulares” que autoriza el DEBERÍA. Posiblemente esto debería ser especificado con un DEBE, debido a que la cláusula “a menos que” también aplicaría, pero este estilo de especificación es bastante común.
Otra trampa bastante común es saltarse la especificación buscando ejemplos, e implementar lo que dice ahí.
Desafortunadamente, los ejemplos típicamente toman la menor cantidad de atención de los autores, debido a que deben ser actualizados por cualquier cambio al protocolo.
Como resultado, habitualmente son las partes menos confiables de una especificación. Sí, los autores deberían absolutamente doble-chequear los ejemplos antes de la publicación, pero de alguna manera los errores logran pasar.
También, incluso un ejemplo perfecto puede no estar orientado a mostrarte el aspecto del protocolo que andas buscando. Habitualmente están recortados para ser más breves, o son mostrados después que se realiza una etapa de decodificación.
Aunque toma más tiempo, es mejor leer el texto completo. Los ejemplos no son la especificación.
Habitualmente se utiliza BNF Aumentado (“Augmented BNF”) para definir artefactos de protocolos. Por ejemplo:
FooHeader = 1#foo
foo = 1*9DIGIT [ ";" "bar" ]
Una vez que te acostumbras a usar, ABNF ofrece una forma fácil de entender para hacer un bosquejo de cómo deberían verse los elementos de un protocolo.
Sin embargo, ABNF es “aspiracional” – identifica la forma ideal de un mensaje, y los mensajes que generas deberían calzar con eso. No especifica qué hacer con mensajes que recibas que no calcen. De hecho, mucha especificaciones fallan completamente en decir qué relación tiene el ABNF con el procesamiento de requisitos.
La mayor parte de los protocolos fallan muy mal si tratas de forzar su ABNF en forma estricta, pero a veces eso importa. En el ejemplo de arriba, no está permitido espacios en blanco alredesdor del punto y coma, pero puedes apostar que algunas personas lo pondrán, y algunas implementaciones lo aceptarán.
Así que, asegúrate de leer el texto alrededor del ABNF para tener requisitos adicionales, o contexto, y si no hay requisitos directos, puedes tener que ajustar el calce para hacerlo más tolerante a distintas entradas de lo que indica el ABNF.
Algunas especificaciones están comenzando a reconocer el carácter aspiracional de ABNF y están incorporando algoritmos de calce que explícitamente manejan los errores. Si están especificados, deberían ser respetados exactamente, para asegurar la interoperabilidad.
Desde el RFC3552, las cláusulas de los RFC han incluido una seccion de “Consideraciones de Seguridad” (“Security Considerations”).
Como resultado, es raro que un RFC sea publicado sin una sección sustancial sobre seguridad; el proceso de revisión no permite que un borrador solo diga “No hay consideraciones de seguridad para este protocolo”.
Así que vale la pena leerlo y asegurarse que entiendes la sección de Consideraciones de Seguridad, tanto si estás implementando como desplegando el protocolo; si no lo haces, es casi seguro que algo te morderá en el camino.
Seguir las referencias (si hay) también es una buena idea. Si no hay ninguna, trata de buscar algunos de los términos que utiliza, para tener alguna idea de los problemas discutidos.
Si un RFC no responde a tu pregunta, o si no estás seguro de la intención de un texto, el mejor camino a seguir es buscar el Grupo de Trabajo (“Working Group”) más relevante y hacer la consulta en su lista de correo. Si no hay un grupo de trabajo activo que cubra el tópico en cuestión, intenta con la lista de correo de la área más apropiada.
Ingresar una errata no es normalmente el primer paso que deberías tomar –habla con alguien primero-.
Muchos Grupos de Trabajo están usando ahora Github para administrar sus especificaciones; si tienes una pregunta de una especificación que está activa, acude ahí e ingresa un ticket (“issue”). Si ya es un RFC publicado, es mejor usar la lista de correo, a menos que encuentres instrucciones al contrario.
Estoy seguro que hay mucho más que escribir acerca de cómo leer los RFCs, y algunos discutirán lo que he escrito aquí, pero es cómo yo me los imagino. Espero que sea útil.
Next post: El tamaño de los mensajes en DNS (“DNS Flag Day 2020”)
Previous post: Integrating BGPalerter with Pushover reports