Intranet Chat — Описание протоколаДанная статья содержит описание протокола Intranet Chat и призвана облегчить труд разработчиков програмного обеспечения для этого приложения. Вся информация относительно протокола получена из открытых источников, а также с помощью анализа сетевого трафика — никакого дизассемблирования программы не проводилось.
ВведениеВо время разработки продуктов для Intranet Chat (он же "ichat") автор статьи столкнулся с проблемой — отсутствие поддержки. Я думаю многим известно, что автор популярного в домашних сетях чата — Ворожун Александр — по всей видимости давно потерял интерес к своему детищу. По крайней мере именно такое впечатление складывается у пользователей при попытках получить какую-либо помощь или же ответ на вопрос по работе программы. Естественно, это значительным образом затрудняет разработку и не способствует развитию и улучшению приложения. Столкнувшись с этой проблемой, стороннему разработчику не остаётся иного выбора, кроме как обратиться к «открытым источникам». На данный момент в интернете доступны несколько описаний протокола, но в виду явно вредительской направленности сайтов, содержащих данные публикации, автор не считает возможным на них ссылаться. Более того, эти описания не являются исчерпывающими, поэтому даже если вы знакомы с данными публикациями, в этой статье вы всё равно можете почерпнуть нечто новое. Не исключено также, что в данном описании могут быть допущены неточности. Если вы обнаружили неточность - пожалуйста, сообщите автору. По мере возможностей статья дополняется ссылками на разработанные классы из API и комментариями по их использованию. Последнее, на что хотелось бы обратить внимание — в статье рассматривается только серверная реализация протокола в виду того, что автор не является поклонником широковещательных реализаций чатов и не заинтересован в развитии данной ветви продукта. Формат сообщения-«обёртки»В Intranet Chat формат сообщений, отсылаемых от клиента серверу отличается от формата сообщений, отправляемых от сервера клиенту. Сообщения, которыми обменивается клиент и сервер — это своего рода «обёртка», транспорт для зашифрованных сообщений. Формат «вложенного» сообщения (после расшифровки) также отличается от формата «обёртки». Формат сообщения, передающегося от клиента к серверу: IP-ADDRESS — текстовое представление IP-адреса отправителя.(В дальнейшем мы будем называть строки такого вида IDENT). В IChatAPI для представления IDENT’а используется класс IChatSender. Команда — команда серверу, в данном случае (для обёртки) всегда FORWARD. Получатель — IDENT получателя сообщения или же "*" (звёздочка) в случае, если сообщение адресовано всем. Сообщение — зашифрованное сообщение или пакет сообщений. Формат сообщения, отправляемого сервером клиенту: В IChatAPI клиентское и серверное сообщение-обёртка представляются различными классами. Сообщение, передаваемое клиентом серверу представляется классом IChatForwardMessage, в то время как сообщение от сервера клиенту — классом IChatServerForwardMessage. Оба класса «знают» своё назначение у «умеют» переводить своё содержимое в байтовый массив. Шифруется же сообщение как правило с помощью фабрики сообщений (см. ниже). Формат «внутреннего» сообщенияСообщения-обёртки выполняют лишь транспортную функцию, сервер (в базовом исполнении) использует лишь поля отправителя и получателя. Это позволяет создавать «облегченные» сервера, которым нет необходимости знать «внутренний» формат сообщения, а также уметь что-либо расшифровывать. Однако достигаемый таким путём выигрыш в производительности даётся ценой значительных проблем с безопасностью. Так, не производя никаких действий над сообщениями, сервер не в состоянии определить различного рода «манипуляции» с сообщениями. Для обеспечения должной безопасности сервер должен брать на себя часть работы по анализу возможных угроз, как это сделано, например, в сервере Scepsis. Основную же информацию несут «внутренние» сообщения. Общий формат такого сообщения представлен ниже: В IChatAPI для представления базового формата «внутреннего» сообщения используется класс IChatMessage. Класс является абстрактным, что не позволяет создавать его экземпляры непосредственно, но в то же время этот класс может быть чрезвычайно удобен для проведения базовых операций над сообщениями в тех случаях, когда конкретный тип сообщения не важен. Для каждой конкретной команды существует свой собственный класс, например IChatBoardMessage существует для команды BOARD. У каждой команды существует два способа создания — конструктор и фабричный метод. Различие состоит в том, что конструктор принимает непосредственно список параметров, необходимых для создания сообщения, в то время как фабричный метод (newInstance(…)) принимает в качестве параметра экземпляр типа IChatParameterAccessor, который позволяет реализовать произвольный алгоритм чтения параметров (из произвольных источников — будь то массив параметров, список и т.д.). Каждому классу также соответствует собственный визитор-интерфейс, позволяющий применять алгоритм, зависящий от конкретного типа команды к набору базовых сообщений IChatMessage с помощью метода acceptVisitor(com.web_visage.ichat.IChatMessageVisitor). Все сообщения являются «immutable», то есть не позволяют производить непосредственное изменение своих полей. Для того чтобы «изменить» какое-либо поле, необходимо создать новый экземпляр сообщения или использовать updateXXX(..) методы (которые делают абсолютно то же самое). Такое решение позволяет избежать множество проблем, связанных с многозадачностью, хотя и создаёт некоторый перерасход памяти и может создавать (при неправильном использовании) дополнительную нагрузку на сборщик мусора. «Внутренние» сообщения и их параметрыСписок команд, их параметров и соответствующих им классов приведен ниже (формат {Команда [[0x13][0x13] параметры]}) :
Везде, где используется параметр «имя линии» используются следующие соглашения: для обозначения «общего чата» используется имя линии, равное «iTCniaM»; для обозначения «приватного сообщения» используется имя линии, равное «gsMTCI». В IChatAPI сообщения редко создаются непосредственно. Рекомендуется использовать реализации IChatMessageFactory, например DefaultMessageFactory. Тогда в случае необходимости вы сможете полностью изменить алгоритм создания / шифрования сообщений, просто используя другую фабрику сообщений (именно так была решена задача поддержки видоизменённой «anti hack» версии от John Do). Фабрики сообщений ответственны за создание сообщений по параметрам, восстановлению сообщений из байт-формы, а также преобразование сообщений в байт-форму. Одним из следствий такой организации может быть возможность реализации клиента, для которого будет использоваться иной формат передачи данных (более экономичный, к примеру). Статус сообщения представляется в виде одного символа и означает: Жизненный цикл клиента Intranet ChatПервое, что делает клиент ичата при подключении — как это ни парадоксально — отправляет сообщение об отключении себя от основной линии (для стандартного клиента это аналог выхода со всех линий). Делается это для того чтобы «убрать» устаревшие сведения о клиенте, которые возможно остались после «грязного» выхода (обрыва связи, повисания клиента и т.п.). Вслед за сообщением об отключении следует сообщение о подключении к основной линии чата, поле «получатель» установлено в "*" (звёздочку). Каждый клиент, получивший это сообщение, сигнализирует о своём присутствии на линии, отправляя ответное сообщение о подключении с полем «получатель», установленным в IDENT первого клиента. Получив это сообщение, клиент обновляет список контактов и сигнализирует о подключении (список подключений при старте). Также все клиенты, создававшие линии, отправляют сообщения о создании линии вновь подключившемуся клиенту. После этого можно условно считать фазу подключения завершённой (на самом деле чётких временных рамок этой фазы, насколько нам извезнтно, не установлено). В дальнейшем, клиент периодически отправляет запрос на обновление списка контактов, адресуя его всем пользователям и устанавливая поле «получатель» в "*". Каждый клиент, получивший такой запрос, сигнализирует о своём нахождении на всех линиях, отправляя для каждой линии ответный рефреш-пакет с именем линии и полем «получатель», установленным в значение IDENT-строки первого клиента. Не получив ни одного рефреш-ответа в течении какого-то времени (около трёх минут), пользователь удаляется из списка контактов с выводом сообщения о выходе пользователя. В случае, если клиент получает рефреш ответ после того как пользователь был удалён из списка, пользователь добавляется в список контактов, сообщение о подключении не выводится. Сообщения от пользователя, не находящегося в списке контактов, стандартным клиентом игнорируются. Получив личное сообщение, пользователь отправляет ответ-подтверждение. Если в ответ на личное сообщение ответа не получено, это может означать, что клиент более недоступен и в течении трёх минут будет удалён из списка контактов (при отсутствии ответов на рефреш-запросы). Это может также означать наличие нестандартного клиента, который просто-напросто не генерирует ответных подтверждений. В ответ на запрос статуса клиент отправляет запрашивающему свой статус и строку авто-ответа. При создании линии клиент высылает всем пользователям имя линии и пароль. Каждый клиент должен сам ограничивать возможность присоединения пользователя к линии. Для обновления списка контактов не-общей линии клиент высылает REFRESH-запрос с полем «имя линии» равным имени линии (в отличие от обновления списка контактов для общего чата, где это поле равно "*") и полем «получатель», также равным "*". При создании личного чата клиент отправляет команду CREATE с именем линии, равной строке из семи произвольных цифр. Вслед за этим он отправляет команду CONNECT для присоединения к вновь созданной «линии». Отвечающая сторона также отправляет соответствующий CONNECT-пакет для подключения. Обновление приватного чата осуществляется так же как и обновление для любой другой (не общей) линии. При выходе из чата клиент отправляет сообщение о выходе из общей линии и закрывает соединение. При получении этого сообщения клиенты удаляют отправителя из списка контактов. ЗаключениеАвтор надеется, что данная статья хотя бы немного облегчила понимание работы Intranet Chat и пролила немного света на использование IChatAPI для создания приложений под ичат. Большинство из описанного в жизненном цикле является обобщением собственных наблюдений, а также результатом анализа поведения собственных приложений, таких как iRCha, Scepsis и др. Некоторые из этих наблюдений, как нам кажется, не вполне очевидны и могут сберечь время разработчикам, которые только приступают к освоению Intranet Chat. Если у вас возникли какие либо пожелания, уточнения или комментарии — милости просим на наш форум. |