11 января 1982 года двадцать два специалиста по информатике встретились, чтобы обсудить «компьютерную почту» (ныне известную как "электронная почта"). Среди участников был будущий основатель Sun Microsystems, парень, который сделал Zork, чувак, создавший NTP, и еще один, который убедил правительство платить за Unix. Перед ними стояла задача решить проблему: в ARPANET было 455 хостов, и ситуация выходила из под контроля.
Проблема возникла из-за того, что ARPANET переходил с оригинального протокола NCP на протокол TCP/IP, на котором сейчас существует Интернет. После такого перехода быстро должно было появиться множество объединенных сетей (inter...net), которым требуется иерархическая система доменов, чтобы ARPANET мог резолвить свои домены, а другие сети — свои.
В то время были другие сети: “COMSAT”, “CHAOSNET”, “UCLNET” и “INTELPOSTNET”. Их обслуживали группы университетов и компаний в США, которые хотели обмениваться информацией и которые могли позволить себе арендовать 56 тысяч соединений у телефонной компании и купить PDP-11 для маршрутизации.
В изначальной архитектуре ARPANET главный Центр Сетевой Информации (Network Information Center, NIC) отвечал за специальный файл, в котором содержится список всех хостов сети. Он назывался HOSTS.TXT, аналогично файлу /etc/hosts
в современных Linux или macOS. Любое изменение в сети требовало от NIC подключаться к каждому узлу сети по FTP (протокол, созданный в 1971 году), что сильно повышало нагрузку на инфраструктуру.
Хранение всех хостов Интернета в одном файле, конечно, не могло считаться масштабируемым методом. Но приоритетным вопросом была электронная почта. Она была главной задачей адресации. В итоге, они решили создать иерархическую систему, в которой можно было бы запросить у удаленной системы информацию о домене или наборе доменов. Другими словами: «Решение заключалось в том, чтобы расширить существующий почтовый идентификатор вида 'user@host' до 'user@host.domain', где 'domain' может быть иерархией доменов». Так родился домен.
Важно отметить, что такие архитектурные решения были приняты без каких-либо предположений о том, как домены будут использоваться в будущем. Они были обусловлены лишь простотой реализации: это требовало минимальных изменений в существующих системах. Одним из предложений было формировать почтовый адрес как <user>.<host>@<domain>
. Если бы в почтовых именах тех дней уже не было бы точек, то сегодня вы, возможно, писали бы мне на zack.eager@io
UUCP и Bang Path
Считается, что главная функция операционной системы — это определить несколько разных имен для одного и того же объекта, так чтобы система была постоянно занята слежением за всеми взаимоотношениями между разными именами. Сетевые протоколы, похоже, ведут себя так же.
— Дэвид Д. Кларк, 1982
Еще одно отклоненное предложение заключалось в том, чтобы отделить домен восклицательным знаком. Например, для подключения к хосту ISIA в сети ARPANET нужно было бы написать !ARPA!ISIA. Можно было бы использовать шаблоны: !ARPA!* — все хосты сети ARPANET.
Такой метод адресации не то чтобы безумно отличался от стандарта, наоборот, это была попытка поддержки стандарта. Система разделения с помощью восклицательного знака использовалась в инструменте для передачи данных UUCP, созданном в 1976 году. Если вы читаете эту статью в macOS или Linux, то uucp скорее всего все еще установлен и доступен в терминале.
ARPANET появилась в 1969 году и быстро превратилась в мощный инструмент обмена информацией… среди нескольких университетов и правительственных организаций, которые имели к ней доступ. Интернет в знакомом нам виде стал доступен широкой публике в 1991 году, спустя двадцать один год. Но это не значит, что пользователи компьютеров не обменивались информацией.
В эпоху до интернета использовался способ прямого подключения одной машины к другой через телефонную сеть. Например, если вы хотели послать мне файл, то ваш модем позвонил бы моему модему, и файл был бы передан. Чтобы сделать из этого некое подобие сети, был придуман UUCP.
В этой системе у каждого компьютера был файл со списком хостов, о которых ему известно, их телефонные номера и логин и пароль для хоста. Далее требовалось смастерить маршрут от текущей машины к целевой через набор других хостов:
sw-hosts!digital-lobby!zack
Такой адрес использовался не только для передачи файлов или прямого подключения к компьютеру, но и как адрес электронной почты. В эпоху до почтовых серверов вам не удалось бы послать мне письмо, если мой компьютер был отключен.
В то время, как ARPANET был доступен только топовым университетам, UUCP позволил появиться «подпольному» интернету для простых людей. Он был основой и для Usenet и для системы BBS.
DNS
Система DNS, которую мы до сих пор используем, была предложена в 1983 году. Если сделать DNS-запрос с помощью утилиты dig, например, то ответ будет примерно таким:
;; ANSWER SECTION:
google.com. 299 IN A 172.217.4.206
Это значит, что google.com
доступен по адресу 172.217.4.206
. Как вы наверняка знаете, A
означает, что это запись адреса, которая связывает домен с IPv4-адресом. Число 299 — это время жизни (time to live, TTL), сколько еще секунд эта запись считается валидной пока не понадобится делать новый запрос. Но что такое IN
?
IN
это "Internet". Наряду с другими деталями, это поле пришло из прошлого, в котором было несколько конкурирующих сетей, и им нужно было общаться друг с другом. Другие потенциальные значения для этого поля это CH для CHAOSNET, HS для Hesiod (название сервиса системы Athena). CHAOSNET давно закрылся, но сильно модифицированная версия Athena все еще используется студентами MIT. Список всех классов DNS доступен на сайте IANA, но не удивительно, что обычно используется только один из потенциальных вариантов.
TLD
Вероятность того, что будут созданы какие-то другие TLD крайне низка.
— Джон Постел,
1994
Когда было решено, что доменные имена должны располагаться иерархически, осталось определить корень иерархии. Такой корень обозначается точкой .
. Заканчивать все доменные имена точкой — семантически верно, и такие адреса будут работать в браузере: google.com.
Первым TLD был .arpa
. Пользователи могли использовать свои старые имена хостов ARPANET в период перехода. Например, если моя машина была в прошлом зарегистрирована как hfnet
, то мой новый адрес был бы hfnet.arpa
. Это было временным решением для периода смены системы. Серверным администраторам нужно было принять важное решение: какой из пяти TLD им выбрать? “.com”, “.gov”, “.org”, “.edu” или “.mil”?
Когда говорят, что DNS обладает иерархией, имеют ввиду, что есть набор корневых DNS-серверов, которые отвечают, например, за преобразование .com
в адреса DNS-серверов зоны .com
, которые в свою очередь вернут ответ на вопрос «как добраться до google.com
». Корневая зона DNS состоит из тринадцати кластеров DNS-серверов. Кластеров только 13, потому что больше не помещается в один пакет UDP. Исторически сложилось, что DNS работает через UDP, поэтому ответ на запрос не может быть длиннее 512 байт.
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . "
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: March 23, 2016
; related version of root zone: 2016032301
;
; formerly NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 192.228.79.201
B.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:84::b
;
; FORMERLY C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
. 3600000 NS D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
. 3600000 NS E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
;
; FORMERLY NS.ISC.ORG
;
. 3600000 NS F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
. 3600000 NS G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
;
; FORMERLY AOS.ARL.ARMY.MIL
;
. 3600000 NS H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
. 3600000 NS I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
. 3600000 NS K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
;
; OPERATED BY ICANN
;
. 3600000 NS L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
;
; OPERATED BY WIDE
;
. 3600000 NS M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
; End of file
Корневые DNS-сервера находятся в сейфах, внутри закрытых клеток. На сейфе стоят часы, чтобы проверять, не взломали ли видеонаблюдение. Учитывая, как медленно работает реализация DNSSEC, взлом одного из этих серверов позволит взломщику перенаправить весь трафик части пользователей интернета. Это сценарий крутейшего фильма, который еще ни разу не сняли.
Не удивительно, что DNS-сервера топовых TLD очень редко меняются. 98% запросов в корневые DNS-сервера — это запросы по ошибке, обычно из-за того, что клиенты не кэшируют свои результаты как надо. Это стало такой проблемой, что несколько операторов корневых серверов добавили специальные серверы только для того, чтобы отвечать «уходи!» всем людям, которые делают обратные запросы DNS по своим локальным IP-адресам.
Сервера TLD администрируются разными компаниями и правительствами по всему миру (Verisign отвечает за .com
). Когда вы покупаете домен в зоне .com
, примерно 18 центов уходит в ICANN, и 7 долларов 85 центов уходят в Verisign.
Punycode
Редко бывает, что забавное название, которое разработчики придумали для своего проекта, становится окончательным, публичным именем продукта. Можно назвать базу данных компаний Delaware (потому что в этом штате зарегистрированы все компании), но можно не сомневаться: когда дело дойдет до продакшена, она будет называться CompanyMetadataDatastore. Но изредка, когда все звезды сходятся и начальник в отпуске, одно название пробирается сквозь щель.
Punycode — это система для кодировки юникода в имена доменов. Проблема, которую она решает, проста: как записать 比薩.com, когда все системы интернета построены вокруг алфавита ASCII, в котором самый экзотический символ — это тильда?
Нельзя просто переключить доменные имена на unicode. Исходные документы, которые отвечают за домены, обязывают кодировать их в ASCII. Каждое устройство в интернете за последние сорок лет, включая маршрутизаторы Cisco и Juniper, которые использовались для доставки этой страницы, работают с учетом этого условия.
Сам веб никогда не ограничивался ASCII. Изначально стандартом должен был ISO 8859-1, который включал в себя все символы из ASCII, плюс набор специальных символов вроде ¼ и буквы с диакритическими знаками вроде ä. Но этот стандарт включал только латинские символы.
Это ограничение HTML'а было окончательно исключено в 2007, и в том же году Юникод стал самым популярным стандартом кодирования символов в вебе. Но домены все еще работали через ASCII.
Как вы могли догадаться, Punycode не был первой попыткой решить эту проблему. Вы скорее всего слышали про UTF-8, популярный способ кодирования Юникода в байты (число 8 в названии "UTF-8" означает 8 бит в одном байте). В 2000 несколько членов Инженерного совета Интернета (Internet Engineering Task Force) придумали UTF-5. Идея заключалась в кодировании Юникода пятибитными наборами. Далее каждые пять бит связывались с разрешенным символом (A-V и 0-9) в доменных именах. Например, если бы у меня был сайт про изучение японского языка, то адрес 日本語.com превратился бы в загадочный M5E5M72COA9E.com.
У такого метода есть несколько недостатков. Во-первых, символы A-V и 0-9 используются в закодированном выводе. То есть, если нужно использовать один из этих символов в самом названии домена, то их пришлось бы кодировать как и другие символы. Получаются очень длинные имена, и это серьезная проблема: каждый сегмент домена ограничен 63 символами. Домен на Бирманском языке был бы ограничен 16 символами. Но у всей этой затеи есть интересные последствия, например, Юникод таким образом можно передавать через код Морзе или телеграммой.
Отдельным вопросом было то, как клиенты будут понимать, что домен был закодирован именно таким способом, и можно показывать пользователю реальное имя вместо M5E5M72COA9E.com в адресной строке. Было несколько предложений, например, задействовать неиспользованный бит в ответе DNS. Но это был «последний неиспользованный бит в заголовке», и люди, отвечающие за DNS «очень не хотели отдавать его».
Еще одна идея заключалась в том, чтобы добавлять ra--
в начало каждого доменного имени, которое использует этот способ кодирования. В то время (середина апреля 2000) не было ни одного домена, который начинался бы с ra--
. Я знаю интернет, поэтому уверен: кто-то сразу купил домен с ra—
всем назло сразу после публикации того предложения.
Окончательное решение было принято в 2003 — использовать формат Punycode. Он включал в себя дельта-кодирование, которое помогало значительно укоротить закодированные доменные имена. Дельта-кодирование — это особенно хорошая идея в случае с доменами, потому что скорее всего все символы доменного имени находятся примерно в одной области таблицы Юникода. Например, два символа из Фарси будут ближе друг к другу, чем символ из Фарси и символ из Хинди. Чтобы понять, как это работает, давайте взглянем на пример с такой бессмысленной фразой:
يذؽ
В незакодированном виде это три символа[1610, 1584, 1597]
(на основе их положения в Юникоде). Чтобы закодировать их, давайте сначала отсортируем их (запомнив исходный порядок): [1584, 1597, 1610]
. Теперь можно сохранить наименьшее значение (1584
), и расстояние (дельту) до следующего символа (13
), и до следующего (23
), так что передавать и хранить нужно меньше информации.
Далее Punycode эффективно (очень эффективно!) кодирует эти целые числа в символы, разрешенные в доменных именах, и добавляет xn—
в начало строки чтобы сообщить клиентам о кодировании имени. Можно заметить, что все символы Юникода собраны в конце домена. Они хранят не только значения символов, но и их положение в имени. Например, адрес 熱狗sales.com превратится в xn--sales-r65lm0e.com
. Когда вы вводите символы Юникода в адресную строку браузера, они всегда кодируются таким способом.
Эта трансформация могла бы быть прозрачной, но тогда может возникнуть серьезная проблема безопасности. Есть символы Юникода, которые выводятся на экран идентично существующим символам ASCII. Например, вы скорее всего не заметите разницу между кириллической буквой “а” и латинской “a”). Если я зарегистрирую кириллический аmazon.com (xn--mazon-3ve.com), то вы можете не понять, что находитесь на другом сайте. По этой причине сайт .ws, выглядит в вашем браузере скучно: xn--vi8hiv.ws
.
Протокол
Первая часть URL — это протокол, по которому нужно производить соединение. Самый распространенный протокол это http
. Это простой протокол для передачи документов, который Тим Бернерс-Ли разработал специально для веба. Это был не единственный вариант. Некоторые считали, что нужно использовать Gopher. Gopher был разработан специально для отправки структурированных данных, по аналогии со структурой файлового дерева.
Например, при запросе на /Cars
можно получить такой ответ:
1Chevy Camaro /Archives/cars/cc gopher.cars.com 70
iThe Camero is a classic fake (NULL) 0
iAmerican Muscle car fake (NULL) 0
1Ferrari 451 /Factbook/ferrari/451 gopher.ferrari.net 70
Он представляет два автомобиля, дополнительную мета-информацию о них и указание адреса, где можно получить больше информации. Идея была в том, что клиент обработает эту информацию и приведет ее в удобный вид, где записи связаны с конечными страницами.
Первым популярным протоколом был FTP. Его создали в 1971 году для получения списков и скачивания файлов на удаленных машинах. Gopher был логическим продолжением этой идеи, так как он предлагал похожий листинг, но также включал механизмы получения мета-информации о записях. Это означает, что его можно было использовать и для других задач, вроде ленты новостей или простой базы данных. Однако, ему не хватало свободы и простоты, которые характеризуют HTTP и HTML.
HTTP — это очень простой протокол, особенно по сравнению с альтернативами вроде FTP или даже HTTP/2, популярность которого сегодня растет. Во-первых, HTTP полностью текстовый, в нем не используются магические бинарные элементы (которые могли бы значительно улучшить производительность). Тим Бернерс-Ли правильно решил, что текстовый формат позволит поколениям программистов легче разрабатывать и отлаживать приложения, использующие HTTP.
HTTP также не делает никаких допущений по поводу содержания. Несмотря на то, что он был разработан специально для передачи HTML, он позволяет указать тип содержания (с помощью MIME Content-Type
, который был новым изобретением в свое время). Сам протокол довольно прост.
Запрос:
GET /index.html HTTP/1.1
Host: www.example.com
Возможный ответ:
HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Encoding: UTF-8
Content-Length: 138
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
ETag: "3f80f-1b6-3e1cb03b"
Accept-Ranges: bytes
Connection: close
<html>
<head>
<title>An Example Page</title>
</head>
<body>
Hello World, this is a very simple HTML document.
</body>
</html>
Чтобы уловить контекст, вспомните, что в основе сети лежит IP, протокол интернета. IP отвечает за передачу маленького пакета данных (около 1500 байтов) от одного компьютера другому. Поверх этого — TCP, который отвечает за передачу более крупных блоков данных вроде целых документов или файлов. TCP осуществляет гарантированную доставку с помощью множества IP-пакетов. Поверх этого живет протокол вроде HTTP или FTP, который указывает, какой формат данных использовать для пересылки с помощью TCP (или UDP или другого протокола) чтобы передать осмысленные и понятные данные.
Другими словами, TCP/IP посылает кучу байтов другому компьютеру, а протокол уровня HTTP объясняет, чем являются эти байты и что они означают.
Можно сделать свой протокол, если захочется, собирая байты из сообщений TCP как угодно. Единственное требование заключается в том, чтобы получатель говорил на том же языке. Поэтому принято стандартизировать эти протоколы.
Конечно, существуют менее важные протоколы. Например, есть протокол цитаты дня Quote of The Day (порт 17), и случайного символа Random Characters (порт 19). Они могут показаться забавными сегодня, но они помогают понять важность универсального протокола для передачи документов, которым был HTTP.
Порт
Историю Gopher и HTTP можно проследить по их номерам портов. Gopher — это 70, HTTP 80. Порт для HTTP был установлен (скорее всего Джоном Постелом из IANA) по запросу Тима Бернерса-Ли между 1990 и 1992 годами.
Концепция регистрации «номеров портов» появилась еще до интернета. В оригинальном протоколе NCP, на котором работала сеть ARPANET, удаленные адреса были идентифицированы с помощью 40 битов. Первые 32 указывали на удаленный хост, это похоже на то, как IP работает сегодня. Последние 8 бит назывались также AEN (“Another Eight-bit Number” или «Еще одно восьмибитное число»), и использовались для схожих с портом целей: разделить сообщения, имеющие разные предназначения. Другими словами, адрес указывал на машину, куда нужно доставить сообщение, а AEN (или номер порта) указывал на приложение, которому нужно доставить сообщение.
Они быстро запросили, чтобы пользователи регистрировали эти «номера сокетов» для ограничения потенциальных коллизий. Когда номера портов расширили до 16 бит в TCP/IP, процесс регистрации продолжился.
Не смотря на то, что у протоколов есть порт по умолчанию, имеет смысл позволять вручную указывать порт для упрощения локальной разработки и для работы с несколькими сервисами на одной машине. Такая же логика лежала в основе добавления www.
в адреса сайтов. В то время было сложно представить, чтобы кто-то получал доступ к корню своего домена чтобы всего лишь захостить «экспериментальный» веб-сайт. Но если давать пользователям имя хоста для конкретной машины (dx3.cern.ch
), то начнутся проблемы когда появится необходимость заменить машину. Если использовать общий поддомен (www.cern.ch
), то можно свободно менять место, куда указывает этот адрес.
То, что посередине
Вы, наверное, знаете, что в URL двойной слэш (//
) стоит между протоколом и второй частью:
http://eager.io
Этот двойной слэш был унаследован от компьютерной системы Apollo, которая была одной из первых сетевых рабочих станций. У команды Apollo появилась такая же проблема, что у Тима Бернерса-Ли: им нужен был способ отделять путь от машины, в которой находится этот путь. Они решили придумать специальный формат для пути:
//computername/file/path/as/usual
И сэр Тим скопировал эту схему. На самом деле, теперь он жалеет об этом решении. Он хотел бы, чтобы домен (в нашем примере example.com
) был бы первой частью пути:
http:com/example/foo/bar/baz
Все остальное
Мы рассказали о компонентах URL, которые нужны для подключения к конкретному приложению на удаленной машине где-то в интернете. В продолжении этой статьи мы поговорим про компоненты URL, которые обрабатываются удаленным приложением, чтобы вернуть ответ. Это путь, фрагменты, запросы и авторизация.
Хотелось бы включить все это в один пост, но размер уже начинает пугать читателей. Но вторая часть будет стоить потраченного времени. Мы обсудим альтернативные формы URL, которые обдумывал Тим Бернерс-Ли, историю форм и как придумали синтаксис параметров GET-запроса, и как 15 лет спорят о способе создания неизменяемых URL.
Автор: freetonik