Как стать автором
Обновить

Комментарии 13

рассматривали возможность использования ipv6 тунелирования и teredo?
Честно скажу нет.

Касаемо IPv6 думал посмотреть в его сторону, но потом глянул статистику, а так же предложения провайдеров с IPv6, а так же учитывая тот факт, что даже у меня у самого нет на «внешку» IPv6 решил отложить рассмотрения этого вопроса, на другое время.
Вам в помощь tunnelbroker.net

Бесплатно, без смс :)

Лучше sixxs.net. Бесплатно, без СМС, но есть туннели в России. К тому же умеет динамические адреса и выдаёт подсети. Вот тут о настройке писал: habrahabr.ru/post/203376.
Зачем вообще эти брокеры когда есть192.88.99.1 6to4. Если ip за NAT не меняется то всё оилично работает.
Microsoft, кстати, постепенно отказывается от Teredo. Сервер для Windows 7, например, выключили пару месяцев назад.
А почитать доки по протоколу UPnP это не путь настоящего джедая?
Кстати, а под linux есть решения upnp и nat-pmp итд? Просто в свое время гуглил искал и все было печально.
Есть готовые утилиты miniupnpc (cli) и gupnp-tools (gui).
Правильно ли я понял работу UPnP:
1) создаём магический udp-сокет
2) прикрепляем к нему хитро полученный локальный IP
3) шлём 3 раза с этого хитрого-магического udp-сокета широковещательное (на адрес 239.255.255.250:1900) сообщение-обнаружение
4) роутер отзывается, при этом мы узнаём его ip-адрес, tcp-порт, html/xml-скрипт
5) шлём get-запросы по http на скрипт роутера, при этом ставя UA как «Microsoft-Windows/6.1 UpnP/1.0»
6) через http сначала открываем порт, потом обмениваемся трафиком, потом закрываем
Что я упустил?

p.s. Жалко что у вас 6й пункт не расписан, т.е. сами http-запросы и ответы, суть протокола UPnP.
Но статья ценна, безусловно.
Там HTTP-over-UDP, в общем-то через тот-самый мультикаст-канал.
Спасибо, за оценку, и тому факту, что статья для кого-то оказалось ценной или полезной.

Касаемо Вашего понимая — да, так.

Касаемо 6-го пункта, не стал подробно описывать, потому как в статье под спойлером лежит пример запроса.

Пример открытия порта
public static void AddPort(string webPath, int port, string protocol, string locIP, string desc)
{
	//webPath=http://192.168.0.1:46382/ctl/IPConn
	HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webPath);

	request.Method = "POST";
	request.Headers.Add("Cache-Control", "no-cache");
	request.Headers.Add("Pragma", "no-cache");
	request.Headers.Add("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping\"");

	//request.Connection = "Close";
	request.ContentType = "text/xml; charset=\"utf-8\"";
	request.UserAgent = "Microsoft-Windows/6.1 UPnP/1.0";

	string query = "<?xml version=\"1.0\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body><m:AddPortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\"><NewRemoteHost xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\"></NewRemoteHost><NewExternalPort xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui2\">"+port+"</NewExternalPort><NewProtocol xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">"+protocol+"</NewProtocol><NewInternalPort xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui2\">"+port+"</NewInternalPort><NewInternalClient xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">"+locIP+"</NewInternalClient><NewEnabled xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"boolean\">1</NewEnabled><NewPortMappingDescription xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">"+desc+"</NewPortMappingDescription><NewLeaseDuration xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui4\">0</NewLeaseDuration></m:AddPortMapping></SOAP-ENV:Body></SOAP-ENV:Envelope>";
	
	byte[] data = Encoding.UTF8.GetBytes(query);

	request.ContentLength = data.Length;

	using (Stream stream = request.GetRequestStream())
	{
		stream.Write(data, 0, data.Length);
	}
	string response = String.Empty;
	WebResponse response = request.GetResponse();
	using (StreamReader sr = new StreamReader(response.GetResponseStream()))
	{
		response = sr.ReadToEnd();
	}
}



По факту, в самом запросе сложного нет ничего. Касаемо удаления порта, то делаем это руками, когда сами считаем нужным, либо же можно попробовать покрутить параметр «жизни» открытого порта.

Пример закрытия порта
public static void DeletePort(string webPath, string protocol, int port)
{
	//webPath=http://192.168.0.1:46382/ctl/IPConn
	HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webPath);

	request.Method = "POST";
	request.Headers.Add("Cache-Control", "no-cache");
	request.Headers.Add("Pragma", "no-cache");
	request.Headers.Add("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping\"");

	request.ContentType = "text/xml; charset=\"utf-8\"";
	request.UserAgent = "Microsoft-Windows/6.1 UPnP/1.0";
	
	string query = "<?xml version=\"1.0\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body><m:DeletePortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\"><NewRemoteHost xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\"></NewRemoteHost><NewExternalPort xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui2\">" + port + "</NewExternalPort><NewProtocol xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">" + protocol + "</NewProtocol></m:DeletePortMapping></SOAP-ENV:Body></SOAP-ENV:Envelope>"; 

	byte[] data = Encoding.UTF8.GetBytes(query);

	request.ContentLength = data.Length;

	using (Stream stream = request.GetRequestStream())
	{
		stream.Write(data, 0, data.Length);
	}

	string responseString = String.Empty;
	WebResponse response = request.GetResponse();
	using (StreamReader sr = new StreamReader(response.GetResponseStream()))
	{
		responseString = sr.ReadToEnd();
	}
}


Отдельно, стоит заметить, что если порт уже был удален, то будет возвращено «Ошибка 500».

Если считаете, что данные примеры, и пример запроса у роутера внешнего IP, следует добавить в статью — напишите, можно в личку, и я дополню статью.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории