Подтвердить что ты не робот

Пример пробивки отверстий Java UDP - подключение через брандмауэр

Допустим, у меня два компьютера.

Они знают публичные и частные IP-адреса друг друга через ice4j.

Один клиент прослушивает, а другой отправляет некоторую строку.

Я бы хотел, чтобы это произошло через перфорирование отверстий UPD:

Let A be the client requesting the connection

Let B be the client that is responding to the request

Let S be the ice4j STUN server that they contact to initiate the connection
--
A sends a connection request to S

S responds with B IP and port info, and sends A IP and port info to B

A sends a UDP packet to B, which B router firewall drops but it still
punches a hole in A own firewall where B can connect

B sends a UDP packet to A, that both punches a hole in their own firewall,
and reaches A through the hole that they punched in their own firewall

A and B can now communicate through their established connection without 
the help of S

Может ли кто-нибудь опубликовать псевдо-примеры того, как делать пробивку отверстий через симметричный NAT? Предполагая, что будет сервер S, который поможет угадать номера портов и установить соединение между клиентом A и B.

Было бы неплохо, если бы вы учитывали двойной NAT.

Примечание:

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

Как только один клиент идентифицирует другого с помощью уникального идентификатора на сервере, ему будет предоставлена ​​другая информация о IP-адресе клиента в отверстие UDP, чтобы перенести данные, необходимые для отправки и получения.

Небольшое обновление:

Есть библиотека, которая появляется на горизонте, чтобы проверить Java:
https://github.com/htwg/UCE#readme

4b9b3361

Ответ 1

Этот пример находится на С#, а не на Java, но понятия обхода NAT являются агностическими.

См. сетевую библиотеку Майкла Лидгрена, которая имеет встроенный NAT-траверс.

Ссылка: http://code.google.com/p/lidgren-network-gen3/ Конкретный С# File Dealing с NAT Traversal: http://code.google.com/p/lidgren-network-gen3/source/browse/trunk/Lidgren.Network/NetNatIntroduction.cs

Процесс, который вы опубликовали, является правильным. Он будет работать только для 3 из 4 общих типов устройств (я говорю, что общее поведение NAT не стандартизировано): Full- Cone NAT, NAT с ограниченным конусом и NAT с ограниченным доступом. Обход NAT не будет работать с Symmetric NAT, которые обычно используются в корпоративных сетях для повышения безопасности. Если одна сторона использует Symmetric NAT, а другая сторона - нет, все равно можно пройти через NAT, но это требует дополнительных догадок. Симметричный NAT для симметричного обхода NAT чрезвычайно сложный - вы можете прочитать статью об этом здесь.

Но на самом деле процесс, который вы описали, работает точно. Я реализовал его для мою собственную программу совместного использования экрана (также, к сожалению, на С#). Просто убедитесь, что вы отключили брандмауэр Windows (если используете Windows) и сторонние брандмауэры. Но да, я могу с радостью подтвердить, что это сработает.

Разъяснение процесса обхода NAT

Я пишу это обновление, чтобы прояснить процесс обхода NAT для вас и будущих читателей. Надеюсь, это может быть ясное резюме истории и процесса.

Некоторые источники ссылок: http://think-like-a-computer.com/2011/09/16/types-of-nat/ и http://en.wikipedia.org/wiki/Network_address_translation, http://en.wikipedia.org/wiki/IPv4, http://en.wikipedia.org/wiki/IPv4_address_exhaustion.

Адреса IPv4 с возможностью уникального имени примерно 4,3 миллиарда компьютеров закончились. Умные люди предвидели эту проблему и, помимо прочего, изобрели маршрутизаторы для борьбы с исчерпанием адреса IPv4, назначив сеть компьютеров, подключенных к себе 1 общим IP-адресом.

Есть IP-адреса локальной сети. И тогда есть WAN IP. IP-адреса локальной сети - это IP-адреса локальной сети, которые уникально идентифицируют компьютеры в локальной сети, например, настольные компьютеры, ноутбуки, принтеры и смартфоны, подключенные к домашнему маршрутизатору. IP-адреса WAN однозначно идентифицируют компьютеры за пределами локальной сети в глобальной сети - обычно это означает Интернет. Таким образом, эти маршрутизаторы назначают группу компьютеров 1 WAN IP. Каждый компьютер по-прежнему имеет свой собственный IP-адрес локальной сети. Сетевые IP-адреса - это то, что вы видите при вводе ipconfig в командной строке и получите IPv4 Address . . . . . . . . 192.168.1.101. IP-адреса WAN - это то, что вы видите при подключении к cmyip.com и получите 128.120.196.204.

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

Что это касается обхода NAT? Ну, поскольку маршрутизаторы были изобретены, прямые соединения (сквозная связь) были несколько... невозможными, без нескольких хаков. Если у вас есть сеть из 2 компьютеров (компьютер A и компьютер B), как для совместного использования WAN IP 128.120.196.204, к какому компьютеру идет соединение? Я говорю о внешнем компьютере (например, google.com), который инициирует подключение к 128.120.196.204. Ответ таков: никто не знает, и маршрутизатор не работает, поэтому маршрутизатор отключает соединение. Если компьютер А инициирует соединение, скажем, google.com, то это другая история. Затем маршрутизатор помнит, что компьютер A с LAN IP 192.168.1.101 подключился к 74.125.227.64 (google.com). Поскольку пакет запроса "Компьютер" покидает маршрутизатор, маршрутизатор фактически переписывает LAN IP 192.168.1.101 на IP-адрес маршрутизатора WAN 128.120.196.204. Таким образом, когда google.com получает пакет запроса Computer A, он видит IP-адрес отправителя, который повторно писал, а не IP-адрес локальной сети компьютера A (google.com видит 128.120.196.204 как IP-адрес для ответа). Когда, наконец, отвечает google.com, пакет достигает маршрутизатора, маршрутизатор запоминает (он имеет таблицу состояний), что он ожидает ответа от google.com, и он соответствующим образом пересылает пакет на компьютер А.

Другими словами, ваш маршрутизатор не имеет проблем при запуске соединения - ваш маршрутизатор не забудьте переслать ответный пакет обратно на ваш компьютер (через весь этот процесс, описанный выше). Но, когда внешний сервер инициирует соединение с вами, маршрутизатор не может знать, к какому компьютеру было предназначено соединение, поскольку компьютер A и компьютер B совместно используют WAN IP 128.120.196.204... если только нет четкого правила который инструктирует маршрутизатор пересылать все пакеты, первоначально идущие на порт назначения X, теперь перейти на компьютер A, порт назначения Y. Это называется переадресацией портов. К сожалению, если вы планируете использовать переадресацию портов для своих сетевых приложений, это не практично, так как ваши пользователи могут не понимать, как включить его, и могут неохотно включить его, если они считают это угрозой безопасности. UPnP просто ссылается на технологию, которая позволяет вам программно разрешать переадресацию портов. К сожалению, если вы подумываете использовать UPnP для переадресации своих сетевых приложений, это тоже не практично, поскольку UPnP не всегда доступен, и когда он есть, он может не включаться по умолчанию.

Итак, какое решение тогда? Решение состоит в том, чтобы либо проксировать весь ваш трафик на вашем собственном компьютере (который вы тщательно предварительно настроили на глобальную доступность), либо придумать способ бить систему. Первое решение (я считаю) называется TURN и волшебным образом решает все проблемы с подключением ценой предоставления ферме серверов доступной пропускной способности. Второе решение называется NAT traversal, и это то, что мы будем изучать дальше.

Ранее я описал процесс внешнего сервера (например, google.com), инициировавшего подключение к 128.120.196.204. Я сказал, что без того, чтобы маршрутизатор имел определенные правила, чтобы понять, на каком компьютере отправлен запрос на соединение Google, маршрутизатор просто отключит соединение. Это был обобщенный сценарий, и он не является точным, потому что существуют разные типы NAT. (Примечание. Маршрутизатор - это фактическое физическое устройство, которое вы можете сбросить на пол. NAT (Network Address Translation) - это программный процесс, запрограммированный в маршрутизаторе, который помогает сохранять адреса IPv4, такие как деревья). Таким образом, в зависимости от того, какой NAT использует маршрутизатор, сценарии подключения различаются. Маршрутизатор может даже комбинировать процессы NAT.

Существует четыре типа NAT со стандартизованным поведением: NAT Full-Cone, NAT с ограниченным доступом, NAT с ограниченным доступом и симметричные NAT. Помимо этих типов могут существовать другие типы NAT с нестандартизированным поведением, но это реже.

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

Надеясь, что некоторые сетевые гуру исправят/добавят ввод, чтобы мы могли больше узнать об этом таинственном процессе.

Чтобы ответить на ваш вопрос о сборе внешнего IP и порта каждого клиента:

Заголовки всех UDP-пакетов структурированы одинаково с одним исходным IP-адресом и одним исходным портом. Заголовки пакетов UDP не содержат "внутренний" IP-адрес источника и "внешний" IP-источник. Заголовки пакетов UDP содержат только один IP-адрес источника. Если вы хотите получить "внутренний" и "внешний" исходный IP-адрес, вам необходимо отправить внутренний IP-адрес источника как часть вашей полезной нагрузки. Но не похоже, что вам нужен внутренний IP-порт и порт. Похоже, вам нужен только внешний IP и порт, как заявил ваш вопрос. Это означает, что ваше решение просто читать исходный IP-адрес и переносить пакет, как поля, которые они есть.

Ниже приведены два сценария (они ничего не объясняют):

Связь с локальной сетью

Компьютер A имеет IP-адрес сети 192.168.1.101. Компьютер B имеет IP-адрес сети 192.168.1.102. Компьютер A отправляет пакет с порта 3000 на компьютер B на порт 6000. Исходным IP-адресом в пакете UDP будет 192.168.1.101. И это будет единственный IP. "Внешний" здесь не имеет никакого контекста, потому что сеть является чисто локальной сетью. В этом примере широкополосная сеть (например, Интернет) не существует. О портах, хотя, потому что я не уверен в NAT, я не уверен, что порт, вписанный в пакет, будет 3000. Устройство NAT может перезаписать пакетный порт от 3000 до чего-то случайного, такого как 49826. В любом случае, вы должен использовать любой порт, вписанный в пакет, чтобы ответить - это то, что вы должны использовать для ответа. Поэтому в этом примере LAN-связи вам нужно отправить только один IP-адрес LAN, потому что это имеет значение. Вам не нужно беспокоиться о порте - маршрутизатор позаботится об этом для вас. Когда вы получаете пакет, вы собираете только IP и порт, просто читая его с пакета.

Связь с WAN

Компьютер A имеет IP-адрес LAN, опять же, 192.168.1.101. Компьютер B снова имеет IP-адрес сети 192.168.1.102. Оба компьютера A и компьютер B будут совместно использовать WAN IP 128.120.196.204. Сервер S - это сервер, глобально доступный компьютер, скажем, сервер Amazon EC2 с IP-адресом WAN 1.1.1.1. Сервер S может иметь IP-адрес локальной сети, но это не имеет значения. Компьютер B также не имеет значения.

Компьютер A отправляет пакет с порта 3000 на сервер S. На выходе маршрутизатора IP-адрес источника LAN пакета от компьютера A переписывается на WAN-адрес маршрутизатора. Маршрутизатор также перезаписывает исходный порт от 300 до 32981. Что видит сервер S с точки зрения внешнего IP и порта? Сервер S видит 128.120.196.204 как IP, а не 192.168.1.101, а Server S видит 32981 как порт, а не 3000. Хотя это не исходный IP-адрес и порты Компьютер A, используемый для отправки пакета, это правильные IP-адреса и порты для ответа. Когда вы получаете пакет, вы можете знать только WAN IP и перезаписываемый порт. Если это то, что вы хотите (вы просили только внешний IP-порт и порт), тогда вы настроены. В противном случае, если вам также нужен внутренний IP-адрес отправителя, вам нужно было бы передать это как обычные данные отдельно от вашего заголовка.

Код:

Как указано выше (ниже Чтобы ответить на ваш вопрос о сборе внешнего IP-адреса), чтобы собрать внешний IP-адрес и порт каждого клиента, вы просто прочитаете их из пакета. Каждая отправленная датаграмма всегда имеет исходный IP-адрес и исходный порт отправителя; вам даже не нужен модный пользовательский протокол, потому что эти два поля всегда включены - каждый отдельный UDP-пакет должен, по определению, иметь эти два поля.

// Java language
// Buffer for receiving incoming data
byte[] inboundDatagramBuffer = new byte[1024];
DatagramPacket inboundDatagram = new DatagramPacket(inboundDatagramBuffer, inboundDatagramBuffer.length);
// Source IP address
InetAddress sourceAddress = inboundDatagram.getAddress();
// Source port
int sourcePort = inboundDatagram.getPort();
// Actually receive the datagram
socket.receive(inboundDatagram);

Поскольку getAddress() и getPort() могут возвращать либо порт назначения или источника, в зависимости от того, что вы его установили, на машине клиента (отправки), вызовите setAddress() и setPort() на сервер (получение), а на сервере (принимающем) вызовите setAddress() и setPort() обратно на клиентскую (отправляющую) машину. Должен быть способ сделать это в receive(). Пожалуйста, уточните, если это (getAddress() и getPort() не возвращают исходный IP-адрес и ожидаемый порт) - это ваш фактический блокпост. Предполагается, что сервер будет "стандартным" UDP-сервером (это не сервер STUN).

Дальнейшее обновление:

Я прочитал ваше обновление о том, как использовать STUN для передачи IP и порта с одного клиента и передачи его другому? Сервер STUN не предназначен для обмена конечными точками или выполнения NAT-обхода. Сервер STUN предназначен для того, чтобы сообщать вам ваш общедоступный IP-адрес, открытый порт и тип устройства NAT (будь то Full-Cone NAT, NAT с ограниченным доступом или NAT с ограниченным доступом к портам). Я бы назвал сервер посредника ответственным за обмен конечными точками и выполнение фактического обхода NAT "интродуктором". В мой личный проект, Мне действительно не нужно использовать STUN для выполнения NAT-перехода. Мой "интродуктор" (сервер посредника, который вводит клиентов A и B), является стандартным сервером, который прослушивает дейтаграммы UDP. Поскольку оба клиента A и B регистрируются у интродуктора, интродуктор считывает их общедоступный IP-адрес, порт и частный IP-адрес (в случае, если они находятся в локальной сети). Публичный IP считывается с заголовка датаграммы, как и для всех стандартных датаграмм UDP. Частный IP написан как часть полезной нагрузки датаграммы, и интродуктор просто считывает его как часть полезной нагрузки. Итак, о полезности STUN, вам не нужно полагаться на STUN, чтобы получить общедоступный IP-адрес и общедоступный порт каждого из ваших клиентов - любой подключенный сокет может вам это рассказать. Я бы сказал, что STUN полезен только для определения того, на каком типе устройства NAT находится ваш клиент, чтобы вы знали, следует ли выполнять обход NAT (если тип устройства NAT является полнокопированным, ограниченным или ограниченным портом) или выполнить (если тип устройства NAT является симметричным).

Просьба подробно рассказать о своем контрольном блоке: если вы хотите получить рекомендации по передовым методам разработки протокола обмена сообщениями приложений и советы по чтению полей с полученных сообщений упорядоченным и систематическим образом (на основе комментария, который вы разместили ниже), могли бы вы поделиться своим текущим методом?

Ответ 2

Ваш вопрос очень широк - я не могу предложить пример, но следующие ссылки могут помочь (спецификации, библиотеки, образцы и т.д.):

Ответ 3

Ваша проблема не связана с Java. Если вы знаете, как открыть UDP-соединение, этого достаточно. Прочитайте содержание следующей ссылки . Не бойтесь названия, он также охватывает UDP. Остальное - это просто Java-кодирование.

P.S.. В вашем сценарии отсутствует шаг. Оба A и B должны иметь открытое соединение с S, потому что S должен сказать B, что A пытается достичь этого. Если B не имеет открытого соединения с S, невозможно, чтобы A и B могли взаимодействовать вместе.

UPDATE

Ответ Джейсона содержит ошибки и дикие предположения об обходе NAT. Чтобы действительно понять этот вопрос, прочитайте работу, проделанную Сайкат Гуха (mpi-sws.org/~francis/imc05-tcpnat.pdf). Классификация конусов Википедии полностью устарела и вводит в заблуждение.

Ответ 4

STUN в основном работает следующим образом: ваш клиент за брандмауэром подключается к серверу STUN вне брандмауэра. Сервер STUN проверяет пакет, полученный от клиента, и отправляет клиенту ответ, содержащий IP-адрес клиента и порт, как они отображаются на сервере STUN.

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

Обычно STUN используется для настройки медиапотоков через брандмауэры, когда брандмауэр уже открыт для сигнализации трафика. в VoIP: клиент связывается с STUN-сервером, чтобы открыть свой собственный внешний IP-порт и порт для трафика UDP, затем отправляет свой запрос на сигнализацию (SIP INVITE или что-то еще) другому клиенту в хорошо открытом открытом порту, включая его информацию о внешнем UDP-адресе в полезной нагрузке (SDP или что-то еще). Таким образом, как правило, один клиент должен быть доступен через открытый порт для сигнализации для одноранговой связи.