# Internet Protocol over Incidental Medium **IPoIM** - это средство для туннелирования SOCKS5 соединения через среду передачи, которая не предназначена для туннелирования таких соединений. IPoIM разрабатывается в расчёте на то, что к нему можно без больших усилий дописывать расширения на языке C. ## Терминология - **среда передачи** - то, через что передаются данные. Например, расширение `vk-msg` реализует передачу данных через сообщения ВК. Значит, в контексте расширения `vk-msg` "отправить данные через среду передачи" означает "отправить данные, закодировав их в сообщение"; а "принять данные из среды передачи" - "принять сообщение в ВК и декодировать его в двоичный вид". ## Архитектура - **`main.c` - входная точка приложения** - **`event_loop.c`, `event_loop.h` - реализация событийного цикла**\ Здесь реализована асинхронная обработка событий через `epoll` - **`connection.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_.c`, `mediums/medium_.h`**\ Реализация расширений. Например, файлы `medium_vk_msg.c` и `medium_vk_msg.h` реализуют среду передачи `vk-msg`. ## Разработка расширений В своем заголовочном файле расширение должно реализовывать только функцию `void medium__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` - **`void medium_set_ready_to_send(bool state)`** - **Что делает:** - Сообщает IPoIM, готово ли расширение к вызову `cbmed_on_send_data(...)` - **Когда вызывать:** - При изменении готовности к вызову `cbmed_on_send_data(...)` и при инициализации - **Заметки:** - Изначально IPoIM предполагает, что среда передачи недоступна. Поэтому эта функция должна быть вызвана, как только расширение становится готовым к вызову `cbmed_on_send_data(...)` после инициализации - **`void medium_set_max_send_size(int32_t size)`** - **Что делает:** - Сообщает IPoIM, какое максимальное количество байтов можно передать `cbmed_on_send_data(...)` - **Когда вызывать:** - При изменении максимального количества байтов, которое может принять `cbmed_on_send_data(...)` - **Заметки:** - До первого вызова этой функции IPoIM предполагает, что среда передачи может передать 1024 Б за один вызов `cbmed_on_send_data(...)` - Не допускается `size <= 0` - **`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(...)`)