[Из песочницы] Dynamic DNS на C# и Яндекс.API

Сегодня, помимо моего любимого занятия (возиться с Arduino в моем кружке детского творчества), решил я поставить себе сервер (Windows 2012 r2) и использовать его для различных манипуляций. Изучив тонну информации, всё прошло гладко.

Когда, закончил экспериментировать в локальной сети, у меня появился вопрос: «Как можно увидеть свой сервер из внешней сети, если у меня динамический 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.

На этом всё, надеюсь кому-нибудь пригодится сие решение. Жду ваших комментариев и критики.

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

© Habrahabr.ru