При написании различных программ для работы с сетью, особенно p2p систем, время от времени возникает необходимость узнать внешний IP своего компьютера из программы (тот адрес, по которому Ваш компьютер доступен из Большого Интернета). Часто возникает искушение пойти легким путем и использовать внешние WEB-сервисы, которые по http возвращают Вам IP, или самому развернуть таковой. Хотя такой способ решения задачи и работоспособен, он тем не менее, имеет ряд недостатков:

  • При использовании собственного сервера – нужно где-то его держать и поддерживать, заодно и c соответствующим доменным именем. В случае выхода его из строя, или принудительного отзыва домена, вся Ваша p2p сеть выходит из строя.
  • При использовании внешнего сервера – Вы вводите зависимость своей системы как от его работоспособности, так и от формата ответов, который вообще никак не стандартизирован, и который владелец сервера может поменять в любой момент. С теми же последствиями для вашей сети.
  • http, которым советуют пользоваться – базируется на tcp, то есть протокол сравнительно тяжеловесный, требующий установки соединения и тп. В общем, пользоваться можно, но перерасход ресурса как компьютера, так и сети налицо.

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


Поколение WWW сильно удивится, но такой протокол давно существует, стандартизирован и широко применяется в IP-телефонии, и кое-где для других сервисов мультимедиа. Протокол называется STUN и специфицирован в rfc5389.


Это легковесный протокол, основанный на UDP, и при его использовании, получение внешнего IP через STUN не требует установления TCP-соединения и тп. По сравнению с www, это и снижает нагрузку на вычислительные ресурсы, и сокращает время определения адреса. Всего два пакета, каждый примерно 50 байт – и адрес известен.


На самом деле, STUN позволяет не только определить внешний IP, но и исследовать поведение текущего NAT-барьера, который может состоять из нескольких последовательных NAT-устройств. Но расширенные функции STUN выходят за рамки статьи о получении IP адреса, поэтому сосредоточимся на главном.


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


Теперь, когда с протоколом и серверами определились, осталось где-то найти STUN-клиент, который легко интегрировать в Вашу программу. Здесь мы может Вам посоветовать взять из нашего проекта Emercoin файл stun.cpp, содержащий законченную подсистему определения внешнего IP через STUN. Благо что проект Open Source, и распространяется под лицензией GPL.


Файл содержит функцию GetExternalIPbySTUN(), которая псевдослучайным образом в цикле опрашивает список предопределённых STUN-серверов до тех пор, пока не получит ответ от какого-то из них. Таким образом, выход из строя части серверов просто замедлит работу подсистемы, но не приведёт к отказу в обслуживании. А так как список содержит более двухсот серверов, вероятность выхода из строя всех их одновременно представляется ничтожной.


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


Также предприняты меры для анонимизации запросов, путём генерации случайного ID каждого запроса. В комбинации с рассеиванием по серверам, владельцу любого сервера становится практически невозможно «вычислить» Вашу p2p сеть. Он получает менее 0.5% запросов, и не может однозначно отделить Ваши запросы от запросов, исходящих от оборудования IP-телефонии.


Ниже приведёт список известных нам публичных STUN-серверов в формате:

Server:port
iphone-stun.strato-iphone.de:3478
numb.viagenie.ca:3478
sip1.lakedestiny.cordiaip.com:3478
stun.12connect.com:3478
stun.12voip.com:3478
stun.1cbit.ru:3478
stun.1und1.de:3478
stun.2talk.co.nz:3478
stun.2talk.com:3478
stun.3clogic.com:3478
stun.3cx.com:3478
stun.726.com:3478
stun.a-mm.tv:3478
stun.aa.net.uk:3478
stun.aceweb.com:3478
stun.acrobits.cz:3478
stun.acronis.com:3478
stun.actionvoip.com:3478
stun.advfn.com:3478
stun.aeta-audio.com:3478
stun.aeta.com:3478
stun.allflac.com:3478
stun.anlx.net:3478
stun.antisip.com:3478
stun.avigora.com:3478
stun.avigora.fr:3478
stun.b2b2c.ca:3478
stun.bahnhof.net:3478
stun.barracuda.com:3478
stun.bcs2005.net:3478
stun.beam.pro:3478
stun.bitburger.de:3478
stun.bluesip.net:3478
stun.bomgar.com:3478
stun.botonakis.com:3478
stun.budgetphone.nl:3478
stun.budgetsip.com:3478
stun.cablenet-as.net:3478
stun.callromania.ro:3478
stun.callwithus.com:3478
stun.cheapvoip.com:3478
stun.cloopen.com:3478
stun.cognitoys.com:3478
stun.comfi.com:3478
stun.commpeak.com:3478
stun.communigate.com:3478
stun.comrex.com:3478
stun.comtube.com:3478
stun.comtube.ru:3478
stun.connecteddata.com:3478
stun.cope.es:3478
stun.counterpath.com:3478
stun.counterpath.net:3478
stun.crimeastar.net:3478
stun.dcalling.de:3478
stun.demos.ru:3478
stun.demos.su:3478
stun.dls.net:3478
stun.dokom.net:3478
stun.dowlatow.ru:3478
stun.duocom.es:3478
stun.dus.net:3478
stun.e-fon.ch:3478
stun.easemob.com:3478
stun.easycall.pl:3478
stun.easyvoip.com:3478
stun.eibach.de:3478
stun.ekiga.net:3478
stun.ekir.de:3478
stun.elitetele.com:3478
stun.emu.ee:3478
stun.engineeredarts.co.uk:3478
stun.eoni.com:3478
stun.epygi.com:3478
stun.faktortel.com.au:3478
stun.fbsbx.com:3478
stun.fh-stralsund.de:3478
stun.fmbaros.ru:3478
stun.fmo.de:3478
stun.freecall.com:3478
stun.freeswitch.org:3478
stun.freevoipdeal.com:3478
stun.genymotion.com:3478
stun.gmx.de:3478
stun.gmx.net:3478
stun.gnunet.org:3478
stun.gradwell.com:3478
stun.halonet.pl:3478
stun.highfidelity.io:3478
stun.hoiio.com:3478
stun.hosteurope.de:3478
stun.i-stroy.ru:3478
stun.ideasip.com:3478
stun.imweb.io:3478
stun.infra.net:3478
stun.innovaphone.com:3478
stun.instantteleseminar.com:3478
stun.internetcalls.com:3478
stun.intervoip.com:3478
stun.ipcomms.net:3478
stun.ipfire.org:3478
stun.ippi.com:3478
stun.ippi.fr:3478
stun.it1.hr:3478
stun.ivao.aero:3478
stun.jabbim.cz:3478
stun.jumblo.com:3478
stun.justvoip.com:3478
stun.kaospilot.dk:3478
stun.kaseya.com:3478
stun.kaznpu.kz:3478
stun.kiwilink.co.nz:3478
stun.kuaibo.com:3478
stun.l.google.com:19302
stun.lamobo.org:3478
stun.levigo.de:3478
stun.lindab.com:3478
stun.linphone.org:3478
stun.linx.net:3478
stun.liveo.fr:3478
stun.lowratevoip.com:3478
stun.lundimatin.fr:3478
stun.maestroconference.com:3478
stun.mangotele.com:3478
stun.mgn.ru:3478
stun.mit.de:3478
stun.miwifi.com:3478
stun.mixer.com:3478
stun.modulus.gr:3478
stun.mrmondialisation.org:3478
stun.myfreecams.com:3478
stun.myvoiptraffic.com:3478
stun.mywatson.it:3478
stun.nacsworld.com:3478
stun.nas.net:3478
stun.nautile.nc:3478
stun.netappel.com:3478
stun.nextcloud.com:3478
stun.nfon.net:3478
stun.ngine.de:3478
stun.noblogs.org:3478
stun.node4.co.uk:3478
stun.nonoh.net:3478
stun.nottingham.ac.uk:3478
stun.nova.is:3478
stun.onesuite.com:3478
stun.onthenet.com.au:3478
stun.ooma.com:3478
stun.oovoo.com:3478
stun.ozekiphone.com:3478
stun.personal-voip.de:3478
stun.petcube.com:3478
stun.pexip.com:3478
stun.phone.com:3478
stun.pidgin.im:3478
stun.pjsip.org:3478
stun.planete.net:3478
stun.poivy.com:3478
stun.powervoip.com:3478
stun.ppdi.com:3478
stun.rackco.com:3478
stun.redworks.nl:3478
stun.ringostat.com:3478
stun.rmf.pl:3478
stun.rockenstein.de:3478
stun.rolmail.net:3478
stun.rudtp.ru:3478
stun.russian-club.net:3478
stun.rynga.com:3478
stun.sainf.ru:3478
stun.schlund.de:3478
stun.sigmavoip.com:3478
stun.sip.us:3478
stun.sipdiscount.com:3478
stun.sipgate.net:10000
stun.sipgate.net:3478
stun.siplogin.de:3478
stun.sipnet.net:3478
stun.sipnet.ru:3478
stun.siportal.it:3478
stun.sippeer.dk:3478
stun.siptraffic.com:3478
stun.sma.de:3478
stun.smartvoip.com:3478
stun.smsdiscount.com:3478
stun.snafu.de:3478
stun.solcon.nl:3478
stun.solnet.ch:3478
stun.sonetel.com:3478
stun.sonetel.net:3478
stun.sovtest.ru:3478
stun.speedy.com.ar:3478
stun.spoiltheprincess.com:3478
stun.srce.hr:3478
stun.ssl7.net:3478
stun.stunprotocol.org:3478
stun.swissquote.com:3478
stun.t-online.de:3478
stun.talks.by:3478
stun.tel.lu:3478
stun.telbo.com:3478
stun.telefacil.com:3478
stun.threema.ch:3478
stun.tng.de:3478
stun.trueconf.ru:3478
stun.twt.it:3478
stun.ucsb.edu:3478
stun.ucw.cz:3478
stun.uiscom.ru:3478
stun.uls.co.za:3478
stun.unseen.is:3478
stun.up.edu.ph:3478
stun.usfamily.net:3478
stun.uucall.com:3478
stun.veoh.com:3478
stun.vipgroup.net:3478
stun.viva.gr:3478
stun.vivox.com:3478
stun.vline.com:3478
stun.vmi.se:3478
stun.vo.lu:3478
stun.vodafone.ro:3478
stun.voicetrading.com:3478
stun.voip.aebc.com:3478
stun.voip.blackberry.com:3478
stun.voip.eutelia.it:3478
stun.voiparound.com:3478
stun.voipblast.com:3478
stun.voipbuster.com:3478
stun.voipbusterpro.com:3478
stun.voipcheap.co.uk:3478
stun.voipcheap.com:3478
stun.voipdiscount.com:3478
stun.voipfibre.com:3478
stun.voipgain.com:3478
stun.voipgate.com:3478
stun.voipinfocenter.com:3478
stun.voipplanet.nl:3478
stun.voippro.com:3478
stun.voipraider.com:3478
stun.voipstunt.com:3478
stun.voipwise.com:3478
stun.voipzoom.com:3478
stun.voxgratia.org:3478
stun.voxox.com:3478
stun.voztele.com:3478
stun.wcoil.com:3478
stun.webcalldirect.com:3478
stun.whc.net:3478
stun.whoi.edu:3478
stun.wifirst.net:3478
stun.wtfismyip.com:3478
stun.wwdl.net:3478
stun.xn----8sbcoa5btidn9i.xn--p1ai:3478
stun.xten.com:3478
stun.xtratelecom.es:3478
stun.yy.com:3478
stun.zadarma.com:3478
stun.zepter.ru:3478
stun.zoiper.com:3478
stun1.faktortel.com.au:3478
stun2.faktortel.com.au:3478
stun1.l.google.com:19302
stun2.l.google.com:19302
stun3.l.google.com:19302
stun4.l.google.com:19302

Комментарии (31)


  1. Chugumoto
    31.08.2017 13:01
    +2

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

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

    как-то не логично же. от чего ушли — к тому и вернулись…
    что зависим от чьих-то серверов. компании обанкротились. сервера перестали существовать и всё — наша система перестала работать


    1. EShumilov
      31.08.2017 13:25

      Какие-то сервера перестанут существовать. Какие-то появятся. Но их сейчас в сети так много, что выпадение некоторых из них не приведет к отказу системы. Образно говоря, мы «падаем на хвост» существующей, устоявшейся инфраструктуре, и используем её возможности.


      1. Chugumoto
        31.08.2017 13:39

        а каким образом обеспечивать актуальность списка?
        он ведь не статичен…
        а то и по первой цитате ж тоже можно актуализировать просто и всё…


        1. EShumilov
          31.08.2017 14:09

          Конкретно мы ищем STUN сервера в сети, актуализируем файл stun.cpp перед выходом новых версий кошелька (обычно 3-4 раз в год). Этого хватает с запасом, так как за год выпадает где-то 10-15% из первоначального списка.


          1. Chugumoto
            31.08.2017 15:25
            +1

            а не лучше ли этот список например в конфиг вынести какой? тогда для обновления списка не будет нужды в пересборке…


            1. olegarch Автор
              31.08.2017 17:58

              Хороший вопрос. Мы тоже рассматривали такую возможность, но всё-таки предпочли держать список в виде статического массива. Доводы в пользу этого подхода такие:

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

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

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

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


              1. pavel_pimenov
                01.09.2017 08:22

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


                1. olegarch Автор
                  01.09.2017 17:09
                  +1

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


                  1. pavel_pimenov
                    01.09.2017 17:28

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

                    А как быть если вам нужно добавить новые 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. olegarch Автор
                      01.09.2017 18:00

                      1. Про обновления списка.
                      Мы это делаем примерно раз в полгода-год, при выпуске очередной версии программы. На самом деле, поддержка актуальности списка не критична, так как даже если 50% серверов «умрёт», то клиент в среднем после 2х попыток обхода получит искомый IP. Как видите, запас прочности системы колоссальный. В реальности же список актуальных серверов изменяется на ~10% в год, так что беспокоиться просто не о чем.
                      Ну а чтоб размер списка держать простым — это несложно. Всегда можно его уменьшить до ближайшего простого, выбросив несколько записей.

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


                      1. pavel_pimenov
                        01.09.2017 18:13

                        У вас assert(false) в релиз билде не вырезается препроцессором?


                        1. olegarch Автор
                          01.09.2017 18:36

                          Судя по комментам — не вырезается. Но мы проверим, спасибо.


    1. vesper-bot
      31.08.2017 13:26
      +2

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


      1. Chugumoto
        31.08.2017 13:38

        у IPv6 тоже ж NAT есть и серые адреса…


        1. vesper-bot
          31.08.2017 16:14

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


      1. Evengard
        31.08.2017 14:34

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


        1. vesper-bot
          31.08.2017 16:16

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


  1. Arahnid
    31.08.2017 13:45

    На картинке, чуть левее блока NAT1, написано: «Добмашняя сеть»


    1. EShumilov
      31.08.2017 14:17

      Спасибо! Исправил.


  1. bopoh13
    31.08.2017 13:47
    -4

    1. olegarch Автор
      31.08.2017 18:38
      +3

      bopoh13 привёл хороший пример того, как делать не надо.
      Про недостатки такого подхода как раз и написано в первом абзаце статьи.
      В приведённом примере используется единственный внешний WEB-сервер checkip.dyndns.org, с не-стандартизованным ответом. Если хозяевам сервиса вдруг вздумается поменять формат ответа, или просто прекратить его деятельность — все пользователи Mikrotik разом получат массу впечатлений.


      1. bopoh13
        31.08.2017 23:10

        Спасибо, что обратили внимание. 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";


  1. molnij
    31.08.2017 20:11

    Здесь мы может Вам посоветовать взять из нашего проекта Emercoin файл stun.cpp, содержащий законченную подсистему определения внешнего IP через STUN. Благо что проект Open Source, и распространяется под лицензией GPL.


    Я ошибаюсь, или подобное действие повлечет за собой необходимость раскрытия кода всего своего приложения? GPL, если мне не изменяет память, весьма своеобразная лицензия…


    1. olegarch Автор
      31.08.2017 20:50

      Формально говоря — да. А на практике — всякое бывает. Например MacOS содержит в себе gzip, который распространяется под GPL.
      А Apple и не думает открывать все коды своей MacOS.


      1. notorca
        01.09.2017 02:10

        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.


  1. slavae
    01.09.2017 17:14

    Почему бы вам просто не поставить этот сервис у себя на сервере?
    Почему надо хардкодить список серверов, если можно скачивать его со своего сервера при каких-то условиях?


    1. olegarch Автор
      01.09.2017 17:20

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

      На самом деле, у нас сеть — подчёркнуто децентрализована, и создавать в ней зависимость от какого-либо сервера у нас нет желания.


  1. maxzhurkin
    02.09.2017 12:39

    У вас математика неправильная: то, что серверов больше двухсот, не значит, что каждый из них получает меньше 0,5% запросов. Утверждение будет верным только если гарантировано равное количество запросов на все сервера, но случайный выбор сервера этого не гарантирует.


    1. olegarch Автор
      02.09.2017 16:13

      Математики в статье вообще не было. Было написано о некоем случайном сервере: «Он получает менее 0.5% запросов». То есть — подразумевалась именно средняя величина, при достаточно большом числе запросов n (математически говоря, стремящемся к бесконечности). А она для текущего списка равно 1/241, и меньше 0.5%, как и заявлено в тексте статьи.
      Это конечно не исключает, что в какие-то моменты времени некий сервер случайно наберёт и более 0.5% запросов. Но в среднем этот процент будет сходиться к 0.041.


      1. maxzhurkin
        02.09.2017 18:24

        И всё же, статистика так не работает.
        Ваше утверждение верно только для больших масштабов времени.


        1. olegarch Автор
          02.09.2017 20:04

          Ну раз Вы так упорно апеллируете к математике и статистике — счас будет.
          Итак, поехали:
          Сервер выбирается из списка размером в 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.