Пример пробивки дырок в Java UDP - подключение через брандмауэр - PullRequest
36 голосов
/ 11 марта 2012

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

Они знают друг другу публичные и частные 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's IP and port info, and sends A's IP and port info to B

A sends a UDP packet to B, which B's router firewall drops but it still
punches a hole in A's 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

Ответы [ 4 ]

52 голосов
/ 12 марта 2012

Этот пример написан на C #, а не на Java, но концепции обхода NAT не зависят от языка.

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

Ссылка: http://code.google.com/p/lidgren-network-gen3/ Конкретный файл C #, работающий с NAT Traversal: http://code.google.com/p/lidgren-network-gen3/source/browse/trunk/Lidgren.Network/NetNatIntroduction.cs

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

Но на самом деле описанный вами процесс работает точно. Я реализовал это для моей собственной программы для совместного использования экрана (также, к сожалению, в C #). Просто убедитесь, что вы отключили брандмауэр 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, назначив сеть компьютеров, подключенных к одному общему IP-адресу.

Существуют IP-адреса локальной сети. И затем есть IP-адреса WAN. 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), которые совместно используют IP-адрес глобальной сети 128.120.196.204, к какому компьютеру подключается?Я говорю о внешнем компьютере (скажем, google.com) , инициирующем соединение с 128.120.196.204.Ответ: никто не знает , как и маршрутизатор, и поэтому маршрутизатор разрывает соединение.Если Компьютер A инициирует соединение, скажем, с google.com, то это другая история.Затем маршрутизатор запоминает, что компьютер A с LAN IP 192.168.1.101 инициировал подключение к 74.125.227.64 (google.com).Когда пакет запроса компьютера А покидает маршрутизатор, маршрутизатор фактически перезаписывает LAN IP 192.168.1.101 в WAN IP маршрутизатора 128.120.196.204.Таким образом, когда google.com получает пакет запроса от компьютера A, он видит IP-адрес отправителя, который переписал маршрутизатор, а не IP-адрес локальной сети компьютера A (google.com видит 128.120.196.204 в качестве IP-адреса для ответа).Когда google.com наконец отвечает, пакет достигает маршрутизатора, маршрутизатор запоминает (у него есть таблица состояний), что он ожидал ответа от google.com, и соответствующим образом пересылает пакет на компьютер A.

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

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

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

Существует четыре типа NAT со стандартизованным поведением: NAT с полным конусом, 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. В любом случае, вы должны использовать любой порт, указанный на пакете, для ответа - это то, что вы должны использовать для ответа. Таким образом, в этом примере связи по локальной сети вам нужно отправить только один IP - IP-адрес локальной сети, потому что это все, что имеет значение. Вам не нужно беспокоиться о порте - маршрутизатор позаботится об этом за вас. Когда вы получаете пакет, вы собираете только IP и порт, просто считывая их из пакета.

WAN Связь

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

Компьютер A отправляет пакет с порта 3000 на сервер S. При выходе из маршрутизатора исходный IP-адрес пакета локальной сети с компьютера A перезаписывается на WAN IP-адрес маршрутизатора. Маршрутизатор также перезаписывает исходный порт от 300 до 32981. Что видит Сервер S с точки зрения внешнего IP-адреса и порта? Сервер S видит 128.120.196.204 в качестве IP-адреса, а не 192.168.1.101, а Сервер 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 (будь то NAT с полным конусом, NAT с ограниченным конусом или NAT с ограниченным портом). Я бы назвал сервер-посредник, отвечающий за обмен конечными точками и выполнение фактического обхода NAT, «представителем». В моем личном проекте мне фактически не нужно использовать STUN для выполнения обхода NAT. Мой «представитель» (сервер-посредник, который представляет клиентов A и B) - это стандартный сервер, который прослушивает дейтаграммы UDP. Поскольку оба клиента A и B регистрируются в интродьюсере, интродюсер считывает их общедоступный IP-адрес, порт и частный IP-адрес (если они находятся в локальной сети). Открытый IP считывается из заголовка дейтаграммы, как и для всех стандартных дейтаграмм UDP. Частный IP записывается как часть полезной нагрузки дейтаграммы, а интродьюсер просто считывает его как часть полезной нагрузки. Итак, что касается полезности STUN, вам не нужно полагаться на STUN для получения общедоступного IP-адреса и публичного порта каждого из ваших клиентов - любой подключенный сокет может сказать вам об этом. Я бы сказал, что STUN полезен только для определения того, под каким типом устройства NAT находится ваш клиент, чтобы вы знали, выполнять ли обход NAT (если тип устройства NAT - Full-Cone, Restricted или Port-Restricted) или выполнять проксирование всего TURN-трафика (если тип устройства NAT симметричный).

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

10 голосов
/ 11 марта 2012

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

6 голосов
/ 20 марта 2012

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

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

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

6 голосов
/ 16 марта 2012

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

PS : в вашем сценарии пропущен шаг.И A, и B должны иметь открытое соединение с S, потому что S должен сообщить B, что A пытается достичь его.Если B не имеет открытого соединения с S, A и B не смогут начать общение вместе.

UPDATE

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

...