12 KiB
Internet Protocol over Incidental Medium
IPoIM - это средство для туннелирования SOCKS5 соединения через среду передачи, которая не предназначена для туннелирования таких соединений. IPoIM разрабатывается в расчёте на то, что к нему можно без больших усилий дописывать расширения на языке C.
Терминология
- среда передачи - то, через что передаются данные. Например, расширение
vk-msgреализует передачу данных через сообщения ВК. Значит, в контексте расширенияvk-msg"отправить данные через среду передачи" означает "отправить данные, закодировав их в сообщение"; а "принять данные из среды передачи" - "принять сообщение в ВК и декодировать его в двоичный вид".
Архитектура
main.c- входная точка приложенияevent_loop.c,event_loop.h- реализация событийного цикла
Здесь реализована асинхронная обработка событий черезepollconnection.c,connection.h- реализация соединения
Здесь реализован контроль за существующим соединением.client.c,client.h- реализация клиента
Здесь реализовано установление соединения с сервером.server.c,server.h- реализация сервера
Здесь реализован сервер.priv_medium.c,priv_medium.h
Здесь реализованы внутренние функции IPoIM, связанные с Medium API. Эти функции не должны вызываться из кода расширений.medium.c,medium.h
Здесь реализованы функции Medium API, которые вызываются из кода расширения.mediums/medium_<NAME>.c,mediums/medium_<NAME>.h
Реализация расширений. Например, файлыmedium_vk_msg.cиmedium_vk_msg.hреализуют среду передачиvk-msg.
Разработка расширений
В своем заголовочном файле расширение должно реализовывать только функцию void medium_<NAME>_setup_callbacks(), которая настроит колбеки (см. далее). Никаких других "публичных" функций у расширения быть не должно, поскольку всё взаимодействие с расширением производится через колбеки, которое оно настраивает.
Medium API
IPoIM реализует функции, которые позволяют расширению общаться с остальным приложением:
void medium_epoll_ctl(int op, int fd, struct epoll_event *ev)- Что делает:
- Вызывает
epoll_ctlдля дескриптора epoll, который отвечает за событийный цикл
- Вызывает
- Когда вызывать:
- Когда нужно добавить, модифицировать или удалить описание файлового дескриптора в epoll
- Заметки:
- Если был вызван
close(...)для файлового дескриптора, то не нужно вызывать эту функцию, чтобы удалить описание дескриптора из epoll. Ядро Linux само удаляет описание из epoll, если дескриптор закрывается
- Если был вызван
- Что делает:
void medium_set_callbacks(/* колбеки в порядке их перечисления в следующей главе */)- Что делает:
- Сообщает IPoIM, какие функции соответствуют каким колбекам
- Когда вызывать:
- Инициализация расширения
- Заметки: нет
- Что делает:
void medium_on_recv_data(const void *data, int32_t size)- Что делает:
- Сообщает IPoIM, что из среды передачи были получены данные
- Когда вызывать:
- При получении данных из среды передачи
- Заметки:
- No-op, если
data == NULLилиsize <= 0
- No-op, если
- Что делает:
void medium_set_ready_to_send(bool state)- Что делает:
- Сообщает IPoIM, готово ли расширение к вызову
cbmed_on_send_data(...)
- Сообщает IPoIM, готово ли расширение к вызову
- Когда вызывать:
- При изменении готовности к вызову
cbmed_on_send_data(...)и при инициализации
- При изменении готовности к вызову
- Заметки:
- Изначально IPoIM предполагает, что среда передачи недоступна. Поэтому эта функция должна быть вызвана, как только расширение становится готовым к вызову
cbmed_on_send_data(...)после инициализации
- Изначально IPoIM предполагает, что среда передачи недоступна. Поэтому эта функция должна быть вызвана, как только расширение становится готовым к вызову
- Что делает:
void medium_set_max_send_size(int32_t size)- Что делает:
- Сообщает IPoIM, какое максимальное количество байтов можно передать
cbmed_on_send_data(...)
- Сообщает IPoIM, какое максимальное количество байтов можно передать
- Когда вызывать:
- При изменении максимального количества байтов, которое может принять
cbmed_on_send_data(...)
- При изменении максимального количества байтов, которое может принять
- Заметки:
- До первого вызова этой функции IPoIM предполагает, что среда передачи может передать 1024 Б за один вызов
cbmed_on_send_data(...) - Не допускается
size <= 0
- До первого вызова этой функции IPoIM предполагает, что среда передачи может передать 1024 Б за один вызов
- Что делает:
void medium_start_timer(uint32_t id, int32_t ms)- Что делает:
- Ставит таймер #
idнаmsмиллисекунд. Отрицательные значенияmsостанавливают таймер
- Ставит таймер #
- Когда вызывать:
- При необходимости выполнить что-либо через какое-либо время по таймеру
- Заметки:
- По истечении таймера будет вызван колбек
cbmed_on_timer(...), а в качестве аргумента будет передано значениеid
- По истечении таймера будет вызван колбек
- Что делает:
Необходимые колбеки
Все колбеки, которые вызывает IPoIM рекомендуется называть с префиксом cbmed_ (callback medium).
Время от времени IPoIM обращается к расширению, чтобы отправить данные. Чтобы IPoIM мог это делать, расширение должно реализовывать следующие колбеки:
bool cbmed_on_init()- Когда вызывается:
- IPoIM хочет инициализировать расширение
- Желаемый исход:
- IPoIM ожидает, что расширение инициализирует всё, что ему нужно для работы, и будет готово к работе
- Аргументы: нет
- Возвращаемое значение:
trueв случае успехаfalseв случае провала
- Заметки:
- эта функция никогда не будет вызвана больше, чем один раз
- до вызова этой функции работа с расширением не производится
- Когда вызывается:
void cbmed_on_deinit()- Когда вызывается:
- IPoIM завершает работу и освобождает ресурсы
- Желаемый исход:
- IPoIM ожидает, что расширение освободит все ресурсы, которые оно выделило
- Аргументы: нет
- Возвращаемое значение: нет
- Заметки:
- эта функция никогда не будет вызвана больше, чем один раз
- после вызова этой функции работа с расширением не производится
- Когда вызывается:
void cbmed_on_timer(uint32_t id)- Когда вызывается:
- Истек таймер, который был запущен функцией
medium_start_timer(...)
- Истек таймер, который был запущен функцией
- Желаемый исход:
- IPoIM ничего не ожидает от расширения, оно может выполнить здесь любые действия
- Аргументы:
id- идентификатор таймера, который использовался вmedium_start_timer(...)
- Возвращаемое значение: нет
- Заметки:
- даже если этот колбек не используется, он всё равно должен существовать, но иметь пустое тело
- Когда вызывается:
void cbmed_on_fd_event(int fd, uint32_t events)- Когда вызывается:
- С файловым дескриптором, добавленным при помощи
medium_epoll_ctl(...), что-то случилось
- С файловым дескриптором, добавленным при помощи
- Желаемый исход:
- IPoIM ожидает, что расширение выполнит нужные действия над файловыми дескрипторами, которые оно добавило в epoll
- Аргументы:
fd- файловый дескрипторevents- битовая маска событий (см.man epoll_ctl)
- Возвращаемое значение: нет
- Заметки: нет
- Когда вызывается:
int32_t cbmed_on_send_data(const void *data, int32_t size)- Когда вызывается:
- IPoIM хочет отправить данные через среду передачи
- Желаемый исход:
- IPoIM ожидает, что после вызова этого колбека, данные отправлены или запланированы к отправке
- Аргументы:
data- двоичные данные, которые нужно отправить через среду передачиsize- размер двоичный данных в байтах
- Возвращаемое значение:
- В случае успеха колбек должен вернуть число байтов, ушедших в среду передачи
- Если передача провалена, то колбек должен -1
- Заметки:
- Гарантируется
size > 0 - Гарантируется
size <= max_send_size(см.medium_set_max_send_size(...)) - Гарантируется, что этот колбек не будет вызван, если расширение сообщило, что оно не готово к отправке данных через среду передачи (см.
medium_set_ready_to_send(...))
- Гарантируется
- Когда вызывается: