Pull to refresh

Comments 32

При использовании внешнего сервера – Вы вводите зависимость своей системы как от его работоспособности, так и от формата ответов, который вообще никак не стандартизирован, и который владелец сервера может поменять в любой момент. С теми же последствиями для вашей сети.

Теперь нужен STUN-сервер, к которому Ваша программа будет отправлять запросы. К счастью, такие сервера есть, каждая уважающая себя VOIP-компания держит таковой.

как-то не логично же. от чего ушли — к тому и вернулись…
что зависим от чьих-то серверов. компании обанкротились. сервера перестали существовать и всё — наша система перестала работать
Какие-то сервера перестанут существовать. Какие-то появятся. Но их сейчас в сети так много, что выпадение некоторых из них не приведет к отказу системы. Образно говоря, мы «падаем на хвост» существующей, устоявшейся инфраструктуре, и используем её возможности.
а каким образом обеспечивать актуальность списка?
он ведь не статичен…
а то и по первой цитате ж тоже можно актуализировать просто и всё…
Конкретно мы ищем STUN сервера в сети, актуализируем файл stun.cpp перед выходом новых версий кошелька (обычно 3-4 раз в год). Этого хватает с запасом, так как за год выпадает где-то 10-15% из первоначального списка.
а не лучше ли этот список например в конфиг вынести какой? тогда для обновления списка не будет нужды в пересборке…
Хороший вопрос. Мы тоже рассматривали такую возможность, но всё-таки предпочли держать список в виде статического массива. Доводы в пользу этого подхода такие:

1. Усложнение установки. Сейчас в поставке имеется только единственный обязательный элемент — бинарная программа, и конфиг — опционален. А при использовании внешнего списка STUNов — надо ещё в правильное место конфиг копировать, и как-то поддерживать его актуальность. Получается «развесистая» структура.

2. Эксплуатанты программы-кошелька — в основном не-админы, и никто не будет заморачиваться постоянным мониторингом актуальности списка. Работает — и ладно. Поэтому на практике, список будет обновляться вместе с обновлением программы. То есть для пользователей вынесение списка во вне пользы не принесёт.

3. Применённый алгоритм обхода списка требует, чтобы количество элементов списка было простым числом. В случае внешнего списка админ вряд ли будет соблюдать это требование, что приведёт к сильному редуцированию количества возможных путей обхода, что снизит балансировку нагрузки и снизит надёжность подсистемы при выходе из строя части серверов.

Но естественно, в других проектах и условия могут быть другими, и там будут резоны держать список в виде внешнего конфига. Но так как исходный текст программы доступен, авторы другого проекта могут творчески переработать наш код, и сделать список загружаемым.
Вопросы.
1. Для чего число серверов должно быть простым?
2. Почему у StunSrv поле char name[30]; а не const char* name;?

1. Чтобы цикл гарантировано обошёл все элементы массива, размер массива N и шаг цикла (step) должны быть взаимно простыми. В противном случае, цикл разваливается на несколько независимых колец. Типичный пример такого разваливания — чётный размер массива и чётный шаг. При таком раскладе, в системе возникает два не-пересекающихся цикла — по чётным и по нечётным элементам. Простейший способ избежать этого — сделать размер массива N простым числом, тогда любой шаг step < N будет по определению взаимно простым. Альтернатива этому — перебирать различные случайные step до тех пор, пока gcd(N, step) != 1. Мы применили простейший вариант, который нас устраивает — с простым N.
2. Ну если нам заранее известны все длины name, мы можем выбрать такую длину, в которую поместятся все имена серверов. В данном случае это 30. Использование char* — это создание указателя на строку, а саму строку держать где-то ещё. Указатель — лишние 8 байт (либо 4 на 32-битовой машине). Средний неиспользуемый хвост строки — меньше. То есть при переходе к архитектуре с указателями, слегка возрастает размер используемой памяти, и без всякой иной пользы для проекта. Соответственно, возникает вопрос уместности такого решения.

Спасибо за подробный ответ. не знал, что вы подсчитали потери памяти так точно.

А как быть если вам нужно добавить новые N STUN сервера? ждете пока число станет простым?
И как быть с «мертвыми» серверами, их нужно исключать из списка.

p.s.
а вы статический анализ С++ кода не используете?
cppcheck — нашел двойное удаление
github.com/emercoin/emercoin/blob/master/src/wallet.cpp#L496
github.com/emercoin/emercoin/blob/master/src/wallet.cpp#L502
и много других ошибок-предупреждений.
1. Про обновления списка.
Мы это делаем примерно раз в полгода-год, при выпуске очередной версии программы. На самом деле, поддержка актуальности списка не критична, так как даже если 50% серверов «умрёт», то клиент в среднем после 2х попыток обхода получит искомый IP. Как видите, запас прочности системы колоссальный. В реальности же список актуальных серверов изменяется на ~10% в год, так что беспокоиться просто не о чем.
Ну а чтоб размер списка держать простым — это несложно. Всегда можно его уменьшить до ближайшего простого, выбросив несколько записей.

2. Про двойное удаление.
Этот код мы как есть импортировали из Биткоина. Если посмотрите, там стоит assert(false), который вызывает exception, то есть до второго удаление дело не доходит. Но за помощь в проекте — спасибо.
У вас assert(false) в релиз билде не вырезается препроцессором?
Судя по комментам — не вырезается. Но мы проверим, спасибо.

По крайней мере нет зависимости от протокола, а используемый STUN-сервер можно и поменять без больших проблем. Кроме того, серьезные компании не могут знать, кто сидит за UDP/STUN пакетом, клиент или кто-нибудь ещё, поэтому не могут НЕ отдать ответ, а их стабильность как целого намного выше стабильности одной отдельно взятой компании. Потом вообще будет как с NTP (ИМХО) — появится децентрализованная публичная служба STUN-ответов, независимая от какого-либо бизнеса. Ну или раньше будет полный IPv6.

Значит, будет IPv6 STUN. Похоже, уже есть. Может, что ещё потом придумают, но скорее всего, этого хватит на сколько-то сот лет.

Странно что эти серьёзные компании не делают для своих клиентов свои проприетарные протоколы как раз для уменьшения издержек от подключения «левых» людей и соотв. траты их трафика.

Почему? Делают, ещё как, та же телефония Cisco всерьез проприетарная, закрытая и всё такое. Просто в случае STUN трафик настолько невелик, что теряется даже в потоке одного SIP-канала, что уж говорить о более толстых вещах, поэтому расходы на него можно "спускать на благотворительность", что-то с этим делать выйдет дороже, чем держать STUN-сервер.

На картинке, чуть левее блока NAT1, написано: «Добмашняя сеть»
bopoh13 привёл хороший пример того, как делать не надо.
Про недостатки такого подхода как раз и написано в первом абзаце статьи.
В приведённом примере используется единственный внешний WEB-сервер checkip.dyndns.org, с не-стандартизованным ответом. Если хозяевам сервиса вдруг вздумается поменять формат ответа, или просто прекратить его деятельность — все пользователи Mikrotik разом получат массу впечатлений.
Спасибо, что обратили внимание. Mikrotik всё-таки решает проблему #1 и #3, и часть проблемы #2:
/ip cloud set ddns-enabled=yes; :delay 2s;
:global currentIP [/ip cloud get public-address];
:if ( [:typeof $currentIP] != "ip" ) do={
  :delay 10s; :set currentIP [/ip cloud get public-address]
}
# /log info message="$currentIP";
Здесь мы может Вам посоветовать взять из нашего проекта Emercoin файл stun.cpp, содержащий законченную подсистему определения внешнего IP через STUN. Благо что проект Open Source, и распространяется под лицензией GPL.


Я ошибаюсь, или подобное действие повлечет за собой необходимость раскрытия кода всего своего приложения? GPL, если мне не изменяет память, весьма своеобразная лицензия…
Формально говоря — да. А на практике — всякое бывает. Например MacOS содержит в себе gzip, который распространяется под GPL.
А Apple и не думает открывать все коды своей MacOS.
HISTORY
The gzip program was originally written by Jean-loup Gailly, licensed under the GNU Public Licence.
Matthew R. Green wrote a simple front end for NetBSD 1.3 distribution media, based on the freely re-distributable redistributable
distributable zlib library. It was enhanced to be mostly feature-compatible with the original GNU gzip
program for NetBSD 2.0.

This implementation of gzip was ported based on the NetBSD gzip, and first appeared in FreeBSD 7.0.

~ uname
Darwin
~ gzip --version
Apple gzip 264.50.1

lorca@defaultvps:$ uname 
Linux
lorca@defaultvps:$ gzip --version
gzip 1.6
Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.
Copyright (C) 1993 Jean-loup Gailly.
This is free software.  You may redistribute copies of it under the terms of
the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
There is NO WARRANTY, to the extent permitted by law.

Written by Jean-loup Gailly.
Почему бы вам просто не поставить этот сервис у себя на сервере?
Почему надо хардкодить список серверов, если можно скачивать его со своего сервера при каких-то условиях?
> Почему бы вам просто не поставить этот сервис у себя на сервере?
Их в мире и так больше 200, зачем ещё один ставить? Не проще воспользоваться готовым?
Кроме того, 200+ независимых серверов — они понадёжнее будут, чем один. Вероятность выхода из строя всех их одновременно пренебрежимо мала.
> Почему надо хардкодить список серверов, если можно скачивать его со своего сервера при каких-то условиях?
И в случае усперной хакерской атаки на наш сервер, или когда к нам придут дяди с паяльником — клиенты скачают незнамо что, и начнут получать такие ответы, которые либо разрушат топологию сети, либо втянут пиринговую сеть под чужой контроль.

На самом деле, у нас сеть — подчёркнуто децентрализована, и создавать в ней зависимость от какого-либо сервера у нас нет желания.
Ничего не помешает дядям с паяльником выцепить список stun-серверов из ваших исходников и запихать их всех в список запрещенных сайтов. То, что они кому-то поломают ip-телефонию при этом — их не остановит.
У вас математика неправильная: то, что серверов больше двухсот, не значит, что каждый из них получает меньше 0,5% запросов. Утверждение будет верным только если гарантировано равное количество запросов на все сервера, но случайный выбор сервера этого не гарантирует.
Математики в статье вообще не было. Было написано о некоем случайном сервере: «Он получает менее 0.5% запросов». То есть — подразумевалась именно средняя величина, при достаточно большом числе запросов n (математически говоря, стремящемся к бесконечности). А она для текущего списка равно 1/241, и меньше 0.5%, как и заявлено в тексте статьи.
Это конечно не исключает, что в какие-то моменты времени некий сервер случайно наберёт и более 0.5% запросов. Но в среднем этот процент будет сходиться к 0.041.
И всё же, статистика так не работает.
Ваше утверждение верно только для больших масштабов времени.
Ну раз Вы так упорно апеллируете к математике и статистике — счас будет.
Итак, поехали:
Сервер выбирается из списка размером в 241 элемент. Вероятность выбора p=1/241.
Распределение равномерное, и обеспечивается криптографически стойким PRNG из библиотеки OpenSSL. Выбор какого-либо конкретного сервера в акте получения IP описывается процессом Бернулли с биноминальным распределением. Соответственно, среднеквадратичное отклонение от матожидания (среднего) для n экспериментов (актов запроса IP-адреса) есть:
сигма=sqrt(n * p * (1 — p))
Нас интересует такой n, после которого количество попаданий на тот же сервер в силу случайности распределения не превысит указанного мною параметра A=0.5%.
В качестве доверительного интервала примем три сигмы.
Пишем неравенство:
n * p + 3 * sqrt(n * p * (1 — p)) < n * A
Смысл неравенства:
количество заходов на сервер плюс тройная сигма меньше заявленного мною параметра A для того же количества заходов n.
Решаем неравенство относительно n, получаем:
n > p * (1 — p) / ((A — p) / 3)^2
Подставив числа, получаем:
n > 51398

Вот так работает статистика. Если желаете опровергнуть — формулы в студию.

Так как STUN у нас уже работает с 2014го, масштабы давно уже соблюдены, и далее будут соблюдаться ещё больше. И кстати, в новой версии программы будет использован тот список STUN-серверов, который приаттачен к данной статье, а там их поболее будет. И для этого списка n > 23764.
Sign up to leave a comment.