Протоколы как средство общения
Обеспечить физическую связь между компьютерами – только половина проблемы. Не менее трудно создать программное обеспечение, работающее в таких жестких условиях. Контроль целостности сообщений и успешности их доставки, согласование различных операционных систем – вот далеко не полный перечень требований, предъявляемых к сетевым приложениям.
Разумеется, существовало множество различных решений, сменяющих друг друга с течением времени. Отбраковывались одни идеи, появлялись другие. Наиболее живучей оказалась клиент – серверная архитектура. Суть ее заключается в следующем: на одном из компьютеров устанавливается специальное программное обеспечение, называемое серверным, а на множестве компьютеров, подключенных к нему – клиентским[42]. Клиент посылает запросы, а сервер в ответ может вернуть запрошенный ресурс или сообщение об ошибке.
Очевидно, клиент и сервер должны придерживаться общих соглашений, иными словами формализовать язык своего общения. Вот это самое соглашение и называется протоколом.
Примером протокола может случить командный язык интерпретатора “command.com”. С его помощью пользователь может управлять файлами и папками своего компьютера. Если попытаться применить ту же схему для взаимодействия с удаленным сервером возникнет необходимость добавить в протокол механизмы установки и управления связью.
Но смешивать различные группы команд в одну кучу непрактично. Поэтому еще на заре развития Internet их решили разделить на отдельные группы, в зависимости от решаемых задач. Так возникли семейства протоколов – множество языков, каждый со своей узкой специализацией, в совокупности обеспечивающих надежную и бесперебойную связь.
Причем один язык ничего не знал о существовании другого, – это обеспечивало полную взаимную независимость. В самом деле, для получения файла с сервера достаточно отдать команду “«Получить Файл» («Имя Файла»)”, совершенно не интересуясь, как и чем было создано соединение между двумя компьютерами, – достаточно лишь знать, что оно есть и все.
В таком случае говорят, что один протокол реализован поверх другого. Можно выделить как минимум два уровня – один протокол, отвечающий за установку соединения, а другой – за передачу команд пользователя и данных.
Примечание: к подобному литературному приему прибегают многие авторы, и у читателей порой возникает вопрос – если протокол всего лишь язык, то, как же он может обеспечивать соединение? Разумеется, никак. Соединение на самом деле обеспечивает программное обеспечение, реализующее протокол данного уровня. Именно на его плечи ложатся все вопросы по контролю и поддержанию связи.
Есть ли необходимость изучать протоколы? Ведь уже написаны десятки готовых приложений, не требующих пользователя ничего, кроме владения мышью. Но редкий клиент использует все возможности протокола, а в любом протоколе, как и в каждом, уважающем себя, языке, есть базовый набор стандартных команд, и впечатляющее множество функций, необязательных для реализации, варьирующихся от сервера к серверу.
Наивно было бы ожидать, от клиентских приложений умения поддерживать нестандартные команды. Все они действуют по стандартной, порой весьма неудобной схеме.
Любой протокол - прежде всего язык, побуждающий к общению – выражению своих мыслей и потребностей в уникальной форме, зависящей от конкретной ситуации. Кроме того, лучший способ понять, как устроена и функционирует сеть – поговорить с ней на ее языке.
Впрочем, если откровенно, то никакого единого общесетевого языка не существует. Для полноценного общения потребуется изучить не один десяток языков, то есть протоколов, после чего по праву можно будет считать себя полиглотом.
Пожалуй, начнем…
Мы похожи на людей, что живут в чужой стране, почти не зная ее языка; им хочется высказать много прекрасных, глубоких мыслей, но они обречены произносить лишь штампованные фразы из разговорника. В их мозгу бродят идеи одна интереснее другой, а сказать эти люди могут разве что «Тетушка нашего садовника позабыла дома свой зонтик».
С. Моэм
Пакеты – кванты информации
В основе языка лежат слова. Слова состоят из букв. Буквы – из звуков. Единицей сетевых сообщений является пакет. Почему не байт? Это бы оказалось слишком расточительным решением: каждый отправляемый байт пришлось бы снабжать заголовком, содержащим, как минимум, адреса получателя и отправителя. Сетевое сообщение, по сути, ничем не отличается от обычного письма. Транзитные узлы изучают конверт и передают его по цепочке друг другу, пока, наконец, оно не окажется у получателя (или возвратится назад, к отправителю).
Таким образом, пакет состоит из конверта, в который при отправлении вкладывается текст самого сообщения. Аналогичным образом получатель извлекает сообщение из конверта. Впрочем, при ближайшем рассмотрении этот процесс оказывается намного сложнее. Как уже было сказано в главе «Протоколы как средство общения», для установки связи приходится прибегать к услугам множества протоколов, каждый из которых ничего не знает обо всех остальных.
Поэтому один протокол не в состоянии интерпретировать заголовок пакета, адресованного другому протоколу. С его точки зрения пакет представляет собой данные неизвестного формата. Он приклеивает к ним свой заголовок и передает пакет очередному протоколу более низкого уровня. Так, в процессе передачи, сообщение все больше и больше «обрастает» служебными данными.
Нечто аналогичное происходит на почте. Отправители пишут письмо и укладывают его в конверт. Почтальоны сортируют письма по близким адресам назначения и запаковывают их в большие мешки, которые собираются с узлов связи и вновь сортируются и укладываются в огромные контейнеры. А у получателя протекает обратный процесс. Протоколы нижнего уровня получают пакет, сверяют заголовок (нам ли он адресован и не был ли поврежден при доставке), и в случае положительного результата, извлекают его содержимое и передают «наверх».
Очередной протокол более высокого уровня проделывает ту же операцию, пока, наконец, из стопки конвертов не выпадет исходное сообщение. Теперь оно может быть обработано прикладным программным обеспечением, даже не подозревающим о том, какой длинный путь прошло сообщение и сколько превращений ему пришлось притереть.
Поскольку манипуляции с заголовками пакетов могут привести к ошибкам и сбоям в обработке сетевых сообщений, большинство операционных систем не разрешает непосредственного доступа к полям заголовка, а предоставляет для их формирования множество высокоуровневых функций API[43], не допускающих задания некорректных значений.
Теоретически сетевое программное обеспечение должно быть готово к любым искажениям заголовка. В самом худшем случае, когда пакет безнадежно поврежден, он должен быть уничтожен или отправлен назад.
К сожалению, в результате ошибок реализации оказывается возможным воздействовать на узлы сети, особыми искажениями заголовка. Например, «завешивать» их.
Бороться с этим можно установкой сетевых фильтров, тщательно проверяющих каждое поле заголовка. В дальнейшем об этом будет рассказано подробнее.
Пакет это минимальная порция информации, которой протоколы обмениваются друг с другом. Он состоит из конверта (заголовка) и сообщения (данных). Пакеты могут многократно вкладываться и извлекаться друг из друга, а при необходимости пакеты могут многократно дробиться, вновь склеиваясь у получателя.
Если некто решит отправить фотографию своему другу, почтовый клиент добавит к ней заголовок с адресами отправителя и получателя, темой сообщения, датой отправки и так далее и передаст сформированный пакет на уровень ниже. Но протокол, ответственный за передачу данных, не может просто дописать свой заголовок и выпустить этот огромный пакет в сеть. Ведь такими темпами не долго начисто блокировать ее работу! Поэтому один большой пакет дробится на множество мелких, перемешивающихся в процессе путешествия со многими другими. На компьютере получателя полученные фрагменты вновь собираются в исходный пакет, из которого прикладной протокол извлекает содержимое сообщения.
Однако, при обсуждении протоколов TCP/IP технически правильно употреблять термин дейтаграмма, вместо слова пакет. Дейтаграмма представляет собой единицу данных, с которой работают протоколы TCP/IP. А термин пакет принято употреблять при описании физического уровня передачи сообщений. Дейтаграмма упаковывается в пакет, причем не обязательно в один. Так, например, при передаче дейтаграмм по X.25 сетям они помещаются в двух пакетах. Впрочем, это лексическое различие достаточно незначительно и в обиходной речи часто говорят «пакет», подразумевая «дейтаграмма».
Дерево протоколов
Прежде чем продолжать повествование о протоколах, необходимо рассмотреть какие задачи приходится решать при установке соединения.
В первую очередь можно назвать маршрутизацию – выбор маршрута, по которому будет отправлен пакет. Ведь получатель может находиться и на другом континенте (и даже в космосе!), соединенный с отправителем множеством подсетей, часть которых в какой-то конкретный момент времени может оказаться неработоспособной, и тогда придется направлять пакеты «объездным» путем.
Но прежде чем отправить пакет в путешествие, надо убедиться, что его размер не парализует сеть свой обработкой. Разбивку одной дейтаграммы на множество пакетов[44] фиксированного размера называют фрагментацией, а противоположный этому процесс - сборкой.
Очевидно, фрагментация влечет за собой необходимость контроля целостности дейтаграммы (все ли пакеты были доставлены) и наличие механизма запросов для повторной пересылки пакетов.
Вообще же для выявления ошибок и сбора информации о работе сети необходим отдельный специализированный механизм, позволяющий находить и по возможности автоматически устранять нарушения работоспособности узлов сети.
Очень важно обеспечить защищенность соединения, как от случайных ошибок, так и преднамеренных атак. Сюда же можно отнести проблемы разделения одного канала между несколькими одновременно работающими приложениями.
Таким образом, вводится понятие виртуального канала, обеспечивающего прозрачную связь между двумя приложениями, защищенную от влияния всех остальных приложений. Например, пользователь может одновременно проверять почту, кликать баннеры, болтая тем временем, с друзьями по ICQ. При этом одно приложение никак не мешает другим (разве что снижает общую скорость).
Все перечисленные операции можно разбить на несколько групп, каждая из которых будет реализована своим протоколом. Очевидно, при этом одни протоколы должны опираться на другие. Так, например, для поддержки виртуального канала необходимо наличие устойчивой связи между узлами.
Поэтому, протоколы можно объединить в семейства в зависимости от круга решаемых ими задач. Тогда сами семейства окажутся связанными между собой простой иерархической зависимостью.
Ниже всех находится так называемый сетевой уровень. В Internet он реализован двумя протоколами IP (Internet Protocol) и ICMP (Internet Control Message Packet).
Протокол IP берет на себя заботы по маршрутизации, фрагментации и сборке пакетов на компьютере получателя. Фактически IP выполняет всю черновую работу по установлению соединения.
К этому же уровню относиться и ICMP протокол, использующийся для передачи сообщений об ошибках и сборе информации о работе сети. На нем основана работа таких утилит, как Ping и TraceRoute, применяющихся для диагностики сети.
Транспортный уровень реализован поверх сетевого. Это означает, что для своих нужд он использует результаты работы протоколов нижнего уровня. В Internet он реализован в протоколах TCP (Transmission Control Protocol) и UDP (User Datagram Protocol). В задачи транспортных протоколов входит обеспечение надежной и достоверной доставки данных через сеть. Сюда же относятся механизмы установки, поддержания и упорядочивания закрытия каналов соединения; обнаружение и устранения неисправностей передачи.
Однако TCP и UDP протоколы функционируют по-разному. Тогда как TCP создает виртуальный канал связи, гарантируя достоверность и надежность сообщений, UDP работает без установки соединения, и всего лишь проверяет контрольную сумму принимаемых дейтаграмм.
Может показаться, что UDP «плохой» протокол. Частично это так и есть, поэтому в подавляющем большинстве случаев используется надежный виртуальный канал связи, создаваемый TCP.
Однако UDP оказывается заметно шустрее TCP, поскольку не требует накладных расходов на поддержание соединения. Он используется, когда необходимость в дополнительном сервисе транспортного уровня отсутствует, а достоверность передачи не требуется. На нем в частности, реализован протокол обращений к DNS (Domain Name Space). В главе «Атака на DNS сервер»[45] будет показано как использовать этот факт для атаки с целью перехвата трафика.
Наконец, прикладной уровень обеспечивает высокоуровневый интерфейс между сетевыми приложениями. Сюда относится множество протоколов работы с почтой (POP3, SMTP, IMAP), сетевыми новостями (NNTP), файлами (FTP) и так далее.
Конечно, это очень грубая схема, но общение представление о функционировании Internet с ее помощью получить можно. В дальнейшем же каждый протокол будет рассмотрен во всех подробностях.
Что такое порт?
Начинать подробное повествование о протоколах невозможно без упоминания портов. Впрочем, читатель наверняка сталкивался с этим понятием и раньше. К сожалению, распространенные учебники пользователя для Internet только добавляют тумана в этом вопросе.
Физические порты ввода-вывода хорошо известны и интуитивно понятны. Может быть, нечто аналогично есть и в Internet? На самом же деле, с сетевой точки зрения порт – не более чем одно из полей заголовка пакета (в действительности их даже два – порт отправителя и порт получателя).
А нужны они затем, чтобы уточнить с каким именно приложением, из всех, установленных на удаленном компьютере, клиент хочет установить связь. Каждое из приложений «закрепляет» за собой один или несколько портов и получает все приходящее пакеты, в заголовках которых прописаны те же значения. Пакет, который никто не забирает, уничтожается, а отправителю возвращается сообщение об ошибке (в этом случае на жаргоне говорят, что «порт закрыт»).
Такая схема обеспечивает совместную работу множества приложений, так, например, на одном и том же компьютере, имеющим всего один IP адрес, могут быть установлены почтовый сервер, сервер новостей, WEB-сервер, FTP-сервер. И никаких конфликтов и разборок «это чей пакет?» между ними не будет.
Очевидно, что приложение-отправитель и приложение-получатель должны использовать общие соглашения. Можно было придумать множество механизмов, обеспечивающих синхронизацию портов отправителя и получателя, но самым простым оказалось закрепить за каждым протоколом определенные порты, заставив разработчиков программного обеспечения придерживаться этого стандарта.
Прочная ассоциация порт-протокол привела к тому, что эти два термина стали частенько путать. Фраза «свяжись с сервером по сто десятому порту» – подразумевает «свяжись с сервером по протоколу POP3». На самом деле, почтовый сервер может быть настроен и на другой порт, значение которого каким-то образом будет сообщено клиенту.
Важно понять, формат передаваемых данных никак не связан со значением порта в заголовке. Выбор порта никак не влияет на протоколы прикладного уровня. Порт это только 16 битное число в заголовке TCP пакета.