Files
ipoim/README.md
2026-03-27 01:04:25 +03:00

12 KiB
Raw Permalink Blame History

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_<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
  • 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(...))