Реинкарнация библиотеки OpenVPN C ++

В Mysterium Network мы работаем над первым в мире децентрализованным VPN. Наш проект построен на Голанге (Go). Go - это статически компилируемый язык, который предлагает богатую стандартную библиотеку. Go синтаксически похож на C, но выходит победителем, когда дело касается безопасности памяти, сборки мусора, структурной типизации и параллелизма в стиле CSP.

Есть много библиотек, написанных на C или C ++. Если вы хотите использовать эти библиотеки в Golang, есть два подхода:

Перепишите библиотеку на Голанге

Несколько проектов прошли по этому пути. Wireguard ® сделал это, ознакомьтесь с некоторыми из их библиотек.

Повторно используйте код так, как это может называть Голанг.

Существуют и другие инструменты, которые могут помочь с вызовом Java-кода или объектного кода C в Golang, но все происходит через посредника. На фундаментальном уровне существует возможность взаимодействия между C и Golang.

В этой статье мы поговорим об интеграции библиотеки C ++ OpenVPN 3 в узел Golang Mysterium.

Как упоминалось выше, мы используем OpenVPN под капотом. Это был наш первый протокол, и он использовался как внешний двоичный файл (исполняемый файл).

В основном это означает, что Mysterium Node и OpenVPN - это два разных процесса, которые взаимодействуют с использованием конфигурации OpenVPN и IPC (точнее, локальных сокетов).

Теперь у этого есть некоторые ограничения - например, распространение программного обеспечения усложняется, поскольку вам также необходимо распространять двоичный файл OpenVPN с каждым узлом Mysterium - два шага, что никогда не подходит для UX.

Это было применимо для проверки концепции или очень ранних версий, но по мере перехода на мобильные платформы этот подход стал очень сложным или даже невозможным, особенно при рассмотрении iOS.

Чтобы решить эту проблему, мы решили найти способ напрямую интегрировать OpenVPN в наш проект Golang. Также мы решили, что этот пакет может быть полезен другим, так и родилась эта библиотека.

Openvpn3 приходит на помощь.

Openvpn3 - это официальная библиотека, поддерживаемая командой OpenVPN, которая используется почти на всех платформах в качестве клиента или соединителя с сервером OpenVPN. Кроме того, он написан на C ++, что привело к возникновению некоторых препятствий, которые нам необходимо было решить.

Голанг и C ++ не ладят

Нашим первым препятствием было то, что код C ++ не может быть напрямую вызван Golang (Cgo, если быть точным).
Нам нужно было внести небольшие изменения в саму библиотеку OpenVPN, чтобы экспортировать клиент OpenVPN как вызываемый код C. Его можно найти здесь, и это в основном совместимая точка входа в библиотеку OpenVPN.

Также есть то, как Golang обрабатывает сам код C (cgo).
Проблема заключалась в том, что Golang и его системы управления пакетами ожидают, что все библиотеки являются исходными файлами (т.е. нет или очень ограниченный двоичный управление пакетами). А процесс сборки библиотеки OpenVPN3 был очень сложным и нелегко выразить на языке Go.

Поэтому мы решили скомпилировать эту библиотеку заранее для всех платформ, которые мы в настоящее время поддерживаем или производим двоичные файлы (семейство arm (android ios), семейство amd64 (Windows, Linux, некоторые симуляторы). Поскольку мы используем Linux для нашей системы автоматической сборки, мы пришлось настроить все компиляторы и SDK в одном месте - но это для другого сообщения в блоге. Подпишитесь на нашу рассылку, чтобы узнать больше о том, что мы создаем.

Наш сильно пропатченный образ докера в значительной степени заимствован у Каралабе. В результате получился один файл заголовка (очень простой) и набор статических библиотек для каждой платформы / ОС, которые нам нужны.

Мы также должны были убедиться, что эти двоичные файлы были Go gettable (удобный способ получить библиотеку с GitHub).
Мы просто передали эти библиотеки в репозиторий Go вместе со всем поддерживающим кодом Go (который доступен по адресу mystery.network/go-openvpn/openvpn3). Не лучший способ распространения программного обеспечения, но нашей целью была готовая библиотека.

Теперь самое простое 😏 - вызвать функции Openvpn3 из Go.

Это довольно легко сделать. Следующие примеры представляют собой простые вызовы функций C, экспортируемых библиотекой OpenVPN (наша оболочка C):

А вот и проблемы:

  1. Прежде всего, строгие правила относительно того, что можно, а что нельзя передавать в код C, и наоборот, например - вы не можете передавать ссылку на функцию go в код C.
  2. Клиент openvpn3 также сильно зависит от функций обратного вызова. Один из способов приблизиться к этому - использовать для обратных вызовов только статические функции. Однако это ограничило бы гибкость и удобство использования библиотеки.
    Гибридное решение заключалось в том, чтобы определить настраиваемые функции обратного вызова в Go и зарегистрировать их на карте с идентификаторами функций. Статические функции в клиенте OpenVPN3 затем будут отправлять соответствующие обратные вызовы зарегистрированным функциям с соответствующими идентификаторами.
    Вот как это работает (возьмем в качестве примера функцию обратного вызова события состояния):

Пользователь определяет обычные структуры go с методами, которые удовлетворяют интерфейсам, ожидаемым реестром обратного вызова:

Структура передается в реестр обратных вызовов, который по сути является глобальным идентификатором - ›карта обратных вызовов:

Что происходит дальше, реестр обратных вызовов вставляет предоставленную пользователем структуру с методами и создает структуру C, готовую для передачи в код C, но вместо передачи ссылки на функцию go в код C он передает идентификатор, который является просто ключом к карте обратного вызова и экспортированная функция go (со специальным комментарием).

Когда код C хочет сообщить пользователю об изменениях состояния, он вызывает статическую функцию go, и одним из параметров является id. Затем этот идентификатор передается в реестр обратных вызовов для поиска и вызова подходящего обратного вызова, определенного пользователем.

Он скомпилирован. По крайней мере, часть Go - это означает, что код C доступен, и все заголовки в порядке.

Большинство драконов начало поднимать голову, когда дело доходило до связывания пакетов Go со статическими библиотеками OpenVPN.

Самая большая проблема заключалась в том, что библиотека была построена с помощью компилятора C ++, но golang cgo по умолчанию использовал компилятор C. В результате все странные и уродливые ошибки стали возникать на этапе линковки. Так что если вы видите похожие ошибки, как в примере - вы не одиноки:

После нескольких часов изучения переполнения стека простым обходным решением было поместить пустой файл .cpp внутрь пакета, который использует импорт C. Таким образом, cgo обманом заставили использовать компоновщик C ++, в котором по умолчанию уже была библиотека C ++.

Мы столкнулись с несколькими другими проблемами, но это снова для другого сообщения в блоге. "Будьте на связи."

В заключение

При использовании новых технологий, таких как Golang, вам иногда приходится отключаться от сети, чтобы найти решения, которые помогут вам использовать существующие библиотеки, чтобы вам не приходилось начинать все с нуля. Однако, как и большинство решений в сфере ИТ, это не серебряная пуля.

Ключевые выводы

  • Предварительно скомпилированные библиотеки сами по себе представляют угрозу безопасности - потенциальные пользователи библиотеки не могут быть уверены, что именно скомпилировано, поскольку нет кода для проверки
  • Каждая комбинация ОС и архитектуры должна иметь отдельную версию одной и той же библиотеки.
  • Проблема с фреймворком iOS - библиотека фреймворка iOS (предоставляемая инструментом gomobile) сама по себе является статической библиотекой. Таким образом, любые другие зависимости связаны, но не объединены в структуру - необходимо сделать это как отдельный шаг.
  • Это просто не лучший вариант - golang обычно ожидает, что весь исходный код, необходимый для пакета, находится в одном месте.

Присоединяйтесь к нашему проекту

Не забудьте подписаться на следующие материалы:

Веб-сайт - https://mysterium.network

Twitter - https://twitter.com/MysteriumNet

Telegram - https://t.me/Mysterium_Network

Reddit - https://www.reddit.com/r/MysteriumNetwork

Facebook - https://www.facebook.com/MysteriumNet

Steemit - https://steemit.com/@mysteriumnetwork

Bitcointalk - https://bitcointalk.org/index.php?topic=1895626.0

Присоединяйтесь к наиболее актуальным для вас группам Telegram и взаимодействуйте с нашей командой. Ждем вашего ответа.

Английский - https://t.me/Mysterium_Network

Правила и FAQ - https://t.me/MysteriumRulesAndFAQ

Объявления - https://t.me/MysteriumOfficialAnnouncements

Тестирование узлов - https://t.me/mysterium_network_nodes

Тестирование MysteriumVPN - https://t.me/joinchat/I5-aG0z_3SA6PLgQBCOXlA

中文 / Китайский - https://t.me/MysteriumChineseChat

русский / русский https://t.me/mystRU

Испанский / испанский - https://t.me/mysterium_network_espanol

И, наконец, если вы хотите видеть больше таких обновлений, дайте нам несколько аплодисментов и дайте нам знать.

* WireGuard »и логотип« WireGuard »являются зарегистрированными товарными знаками Джейсона А. Доненфельда.