Сегодня, помимо моего любимого занятия (возиться с Arduino в моем кружке детского творчества), решил я поставить себе сервер (Windows 2012 r2) и использовать его для различных манипуляций. Изучив тонну информации, всё прошло гладко.
Когда, закончил экспериментировать в локальной сети, у меня появился вопрос: «Как можно увидеть свой сервер из внешней сети, если у меня динамический IP». Снова помогла всемирная книга знаний и были найдены такие сервисы, как DynDNS, no-ip и т.п.
После регистрации увидел, что нужно качать прогу (в роутере настроек под dyndns нет), а как добросовестный параноик, я не люблю ставить сторонний софт. Вспомнив, что имею при себе домен второго уровня делегированный на Яндексе, принялся изучать сторону вопроса, для написания своего софта на C#.
За основу я взял статью «Самодельный Dynamic DNS». Для отправки запросов к API был написан следующий метод:
Теперь нужно было как-то получить внешний IP, а так, как я решил напрочь не пользоваться DynDNS, то и к страничке checkip.dyndns.org решил не обращаться. Начал искать у того же Яндекса. Отправив гет-запрос на страничку yandex.com/internet, нашёл интересную строчку ipv4.internet.yandex.net/internet/api/v0/ip в ответ на такой запрос, мне выдало красивейший IP в чистом виде.
Для понимания откуда взят токен и id, пожалуйста, обратитесь к статье, которую я взял за основу. Таким образом сложились следующие строчки:
Наконец т.к. всё это дело должно работать на сервере, я решил пересоздать консольное приложение в службу. Были добавлены таймеры и проверка на изменение ip относительно предыдущего.
В данный момент при записи нового ip очень много грязнит в лог — можно либо после отладки закомментировать вовсе, либо можно парсить по тегу Error error, тогда можно будет получать чистый ответ от API. Я это не делал, т.к. IP меняется очень редко, а когда всё отлажено, то и ошибки API будут редко.
Таким образом, берём внешний ip мы уже не с постороннего ресурса, а с того-же, где пользуемся доменом, так же код можно запустить на любой windows-машине, вплоть до домашнего компьютера. Но советую тогда переделать проверку IP, так, как сделал автор, на которого я ссылался: ему посоветовали сохранять ip в отдельный файл и брать из него последний, сравнивать с нынешним. Я не делал это потому, что у моего сервера аптайм довольно высокий и перезапуск службы не будет особо часто зачищать переменную outIP.
На этом всё, надеюсь кому-нибудь пригодится сие решение. Жду ваших комментариев и критики.
Когда, закончил экспериментировать в локальной сети, у меня появился вопрос: «Как можно увидеть свой сервер из внешней сети, если у меня динамический IP». Снова помогла всемирная книга знаний и были найдены такие сервисы, как DynDNS, no-ip и т.п.
После регистрации увидел, что нужно качать прогу (в роутере настроек под dyndns нет), а как добросовестный параноик, я не люблю ставить сторонний софт. Вспомнив, что имею при себе домен второго уровня делегированный на Яндексе, принялся изучать сторону вопроса, для написания своего софта на C#.
За основу я взял статью «Самодельный Dynamic DNS». Для отправки запросов к API был написан следующий метод:
static string GET(String getString)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(getString);
request.Method = "GET";
String test = String.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
test = reader.ReadToEnd();
reader.Close();
dataStream.Close();
}
return test;
}
Теперь нужно было как-то получить внешний IP, а так, как я решил напрочь не пользоваться DynDNS, то и к страничке checkip.dyndns.org решил не обращаться. Начал искать у того же Яндекса. Отправив гет-запрос на страничку yandex.com/internet, нашёл интересную строчку ipv4.internet.yandex.net/internet/api/v0/ip в ответ на такой запрос, мне выдало красивейший IP в чистом виде.
Для понимания откуда взят токен и id, пожалуйста, обратитесь к статье, которую я взял за основу. Таким образом сложились следующие строчки:
string ip = GET("http://ipv4.internet.yandex.net/internet/api/v0/ip").Trim('"');
string respons = GET("https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token="
+ token + "&domain="
+ domain + " &subdomain="
+ subdomain + "&record_id="
+ id + "&content="
+ ip);
Наконец т.к. всё это дело должно работать на сервере, я решил пересоздать консольное приложение в службу. Были добавлены таймеры и проверка на изменение ip относительно предыдущего.
using System;
using System.ServiceProcess;
using System.IO;
using System.Net;
using System.Timers;
namespace DDNSyapi
{
public partial class Service1 : ServiceBase
{
String outIp = "";
Timer timer1;
public const string token = "YOURTOKEN";
public const string domain = "YOURDOMAIN";
public const string subdomain ="UPDATINGSUBDOMAIN";
public const string id = "id";
//take token on pddimp.yandex.ru/get_token.xml?domain_name=YOURDOMAIN
//take id on pddimp.yandex.ru/nsapi/get_domain_records.xml?token=YOURTOKEN&domain=YOURDOMAIN
public Service1()
{
InitializeComponent();
}
public static void Logs(string err)
{
StreamWriter txtIst = new StreamWriter(System.IO.Path.GetDirectoryName(
System.Environment.GetCommandLineArgs()[0]) +
"//IpServerLog_" + DateTime.Now.Year +
DateTime.Now.Month + DateTime.Now.Day +
".txt", true);
txtIst.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " : " + err);
txtIst.Close();
}
protected override void OnStart(string[] args)
{
timer1 = new System.Timers.Timer(30 * 60 * 1000);
timer1.Elapsed += timer1_Tick;
timer1.Start();
timer1.Enabled = true;
Logs("Запуск службы");
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
try
{
string ip = GET("http://ipv4.internet.yandex.net/internet/api/v0/ip").Trim('"');
if (!ip.Equals(outIp))
{
outIp = ip;
string respons = GET("https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token="
+ token + "&domain="
+ domain + " &subdomain="
+ subdomain + "&record_id="
+ id + "&content="
+ ip);
Logs(respons);
}
}
catch
{
Logs("проблемы с интернетом..");
}
timer1.Enabled = true;
}
static string GET(String getString)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(getString);
request.Method = "GET";
String test = String.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
test = reader.ReadToEnd();
reader.Close();
dataStream.Close();
}
return test;
}
protected override void OnStop()
{
timer1.Enabled = false;
timer1.Stop();
timer1 = null;
Logs(DateTime.Now.ToString("HH:mm:ss") + " - " + "Остановка службы");
}
}
}
В данный момент при записи нового ip очень много грязнит в лог — можно либо после отладки закомментировать вовсе, либо можно парсить по тегу Error error, тогда можно будет получать чистый ответ от API. Я это не делал, т.к. IP меняется очень редко, а когда всё отлажено, то и ошибки API будут редко.
Таким образом, берём внешний ip мы уже не с постороннего ресурса, а с того-же, где пользуемся доменом, так же код можно запустить на любой windows-машине, вплоть до домашнего компьютера. Но советую тогда переделать проверку IP, так, как сделал автор, на которого я ссылался: ему посоветовали сохранять ip в отдельный файл и брать из него последний, сравнивать с нынешним. Я не делал это потому, что у моего сервера аптайм довольно высокий и перезапуск службы не будет особо часто зачищать переменную outIP.
На этом всё, надеюсь кому-нибудь пригодится сие решение. Жду ваших комментариев и критики.
Поделиться с друзьями
chelaxe
Недавно писал подобное на Node.js, но после непродолжительной работы все сломалось… от сервера стало прилетать 5xx ошибка. Подумал и сделал на djbdns.
sergix
Добрый вечер, пока с пятницы в рабочем состоянии. IP менялось 2 раза, всё нормально. Ещё один раз отваливался интернет на час, наспамило в лог «Проблемы с интернетом», в остальном хорошо)
Ivan_83
Сильно сложно!
Когда то я тоже примерно так же делал только на perl: http://www.netlab.linkpc.net/forum/index.php?topic=11.0
Там же в скриптах есть несколько сервисов которые по хттп тебе отдают твой ip (на nginx такое делается в 4 строчки конфига :) ).
Сейчас всё делает простой скрипт на shell (не баш а переносимый шел)
Аналогичным образом ещё для двух динднсов сделано.
Скрипт запускает крон раз в 5-10 минут.
На то что нужно дёргать запрос только когда IP поменялся я забил, и сервисам и мне комфортно раз в 5 минут.
=============================================================
#!/bin/sh
### Rozhuk Ivan, 2013
### DynDNS update client for dnsexit.com
### version 2.0
# http://www.dnsexit.com/Direct.sv?cmd=ipClients
## Configure variables
HostDomainName=«netlab.linkpc.net www.netlab.linkpc.net»
Login=«xxxxxxxxxx»
Password=«yyyyyyyy»
DynDNSServerName=«update.dnsexit.com»
EnableIPv4=1
EnableIPv6=1
#// DO NOT EDIT BELOW THIS LINE
for i in ${HostDomainName}; do
echo «Updating: ${i}»
if [ $EnableIPv4 -eq 1 ]; then
fetch -4 -q -d -o /dev/stdout «http://${DynDNSServerName}/RemoteUpdate.sv?login=${Login}&password=${Password}&host=${i}»
echo ""
fi
if [ $EnableIPv6 -eq 1 ]; then
fetch -6 -q -d -o /dev/stdout «http://${DynDNSServerName}/RemoteUpdate.sv?login=${Login}&password=${Password}&host=${i}»
echo ""
fi
done
echo «Well done!»
=====================================================================================
Кому лень создавать файл — можно сразу в крон забить вызов fetch/wget и забыть о проблеме.
sergix
Спасибо за ваш вариант!)
Интересно было сделать на c#, для ардуино валялся готовый сервис с гетами( мы готовим робота управляемого ютуб комментариями, скоро сделаю обзор на гиктайме про свой кружок) поэтому 90% кода уже было готово, вписал только сами запросы, т.е. тело функции timer1_Tick. Для меня оказалось очень мелким кодом и вполне порадовало). Однако на шел, без установки фраймворка тоже отличный вариант, только подправить его под яндекс Апи и будет ещё один готовый к использованию скрипт!
Dark_Rider
Тоже самое я сделал на баше и изоленте. без такой кучи кода.
sergix
Я тоже питаю особые чувства к изоленте, а вот с Bourne-Again SHell знаком очень плохо :(
Crazy_as
неплохо, плюсик в карму за изобретательность :)
sergix
Спасибо, надеюсь пригодится кому-то понять Апи или сделать что-то новое :)
vadimgrt
Проверьте реальны отклик на смену Ip. Например, у меня клиенты коннектятся на сервак по доменному имени. Ip сменился. Через сколько минут реально у клиентов обновится ip? я полагаю это же не быстро
sergix
Добрый день, как видите у меня таймер включен на 30 минут, т.е. мне изначально быстрый отклик не был в приоритете. Т.к. меняется у меня раз в неделю и обычно ночью.
Однако я на тестировании включал раз в минуту и яндекс очень быстренько подхватывал — т.е. практически за минуту всё поднималось обратно и яндекс даже не ругался на «тыканье» каждую минуту за ip.
alz72
Я бы посоветовал подключить log4net и писать в лог с его помощью. Это позволит более красиво оформить лог, и разделив уровни логирования ( например где спамит — можно поставить уровень Debug, и выделить в отдельный лог) сделать его более читаемым…
sergix
Уже давно собираюсь продвинуться на новую систему логирования, посмотрю в эту сторону, однако я люблю такие простенькие вещи писать полностью без дополнительных библиотек, в сложном проекте — конечно же буду рассматривать.
alz72
Специально для вас «запилил» инструкцию — надеюсь поможет во внедрении.
sergix
Спасибо, почитаю) Пока в проектах использую логирование через веб(поднят сайт, где принимаются гет запросы и складываются в нужную категорию, а веб морда показывает всё в отличной группировке) и резервно на диск при потере сети, как тут в статье. Если понравится с ним работать, может и попробую)
molnij
Использую похожую связку на powershell, task sheduller'е и godaddy.
redmanmale
Как уже посоветовали выше, используте нормальный логгер, а ещё стоит выложить код на github/bitbucket и причесать.
sergix
Спасибо за интерес :)
побоялся, что сочтут за пиар, код лежит https://github.com/SergeyToff/AutoDNS
Я принял их советы на счет других проектов, в маленькой службе, разве имеет смысл приклеивать тяжелый логгер?
sergix
Возможно вообще стоит писать в журнал windows…
alz72
Логгеры позволяют писать практически во все вообразимые места, тот же log4net умеет писать в том числе и в event log
вот тут есть перечисление с примерами