Last active
December 29, 2015 20:19
-
-
Save ttsuki/7723258 to your computer and use it in GitHub Desktop.
ぼくんちのNASで動いているValue Domain DDNS Updater
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
using System.Xml; | |
using System.Xml.XPath; | |
namespace ValueDomainDDNSUpdater | |
{ | |
class Program | |
{ | |
readonly static string routerHost = "192.168.0.1"; // UPnPルータの場所 | |
readonly static string routerConnection = "WANPPPConn1"; // ルータのインターネット接続名 | |
readonly static string targetDomain = "tu3.jp"; // 管理してるドメイン | |
readonly static string targetHost = "@"; // 更新対象のホスト。他のサブドメインも対象にするときはcname | |
readonly static string updatePassword = "xxxxxxxxxxxx"; // 更新用パスワード | |
static void Main(string[] args) | |
{ | |
int interval = 60; // デフォルト更新間隔 | |
IPAddress registeredIPAddress = new IPAddress(0); | |
while (true) | |
{ | |
Console.Error.Write(DateTime.Now.ToString("[yyyy/MM/dd HH:mm:ss] ") + "Getting my address..."); | |
IPAddress addr = GetExternalAddress(routerHost, routerConnection); // ルータのIPと接続名 | |
if (addr == null) | |
{ | |
// 失敗 | |
Console.Error.WriteLine("failed."); | |
} | |
else if (registeredIPAddress.Equals(addr)) | |
{ | |
// 取得したIPは変わってない。 | |
Console.Error.WriteLine(addr.ToString() + " (not changed)"); | |
} | |
else | |
{ | |
// 取得したIPが変わってる。 | |
Console.Error.WriteLine(addr.ToString() + " (changed)"); | |
// DNSレコード更新リクエスト | |
Console.Error.Write(DateTime.Now.ToString("[yyyy/MM/dd HH:mm:ss] ") + "Updateing DNS Record..."); | |
Exception ex = UpdateValueDomainDDNS(addr, targetDomain, targetHost, updatePassword); | |
if (ex == null) | |
{ | |
// 成功 | |
Console.Error.WriteLine("Updated."); | |
registeredIPAddress = addr; | |
interval = 60; | |
} | |
else | |
{ | |
// 失敗 | |
Console.Error.WriteLine("failed."); | |
interval = Math.Min(interval * 2, 3600); | |
} | |
} | |
Thread.Sleep(interval * 1000); | |
} | |
} | |
/// <summary> | |
/// 指定されたルータにUPnPして、IPアドレス取ってくる | |
/// </summary> | |
/// <param name="routerIPAddress">ルータIP</param> | |
/// <param name="targetConnServiceID">サービスID</param> | |
/// <returns>外部IPAddress。失敗するとnull。</returns> | |
static IPAddress GetExternalAddress(string routerIPAddress, string targetConnServiceID) | |
{ | |
// ルータのデバイス情報のURLを得る。 | |
string deviceUrl = null; | |
{ | |
byte[] request = Encoding.ASCII.GetBytes( | |
@"M-SEARCH * HTTP/1.1 | |
HOST: 239.255.255.250:1900 | |
MAN: ""ssdp:discover"" | |
MX: 3 | |
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1"); | |
/*// 素直な方法は素晴らしいが自分のルータ側のIPが必要だったりするのでめどい | |
var _192_168_0_2 = new System.Net.IPAddress(0x0200A8C0); | |
var _239_255_255_250 = new System.Net.IPAddress(0xFAFFFFEF); | |
using (var client = new UdpClient(new IPEndPoint(_192_168_0_2, 0))) | |
{ | |
client.JoinMulticastGroup(_239_255_255_250); | |
client.Send(request, request.Length, new IPEndPoint(_239_255_255_250, 1900))); | |
IPEndPoint from = null; | |
string ret = Encoding.ASCII.GetString(client.Receive(ref from)); | |
Match m = Regex.Match(ret, @"^location: (.*)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); | |
if (m.Success) { deviceUrl = m.Groups[1].Value; } | |
} | |
/*/ | |
using (var client = new UdpClient()) | |
{ | |
int retry = 3; | |
do | |
{ | |
// ルータにダイレクトアタック!!! | |
client.Send(request, request.Length, routerIPAddress, 1900); | |
for (int i = 0; i < 100; i++) // | |
{ | |
try | |
{ | |
// デバイス情報のURLが書かれた行を抜き出す | |
IPEndPoint from = null; | |
string ret = Encoding.ASCII.GetString(client.Receive(ref from)); | |
Match m = Regex.Match(ret, @"^location: (.*)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); | |
if (m.Success) { deviceUrl = m.Groups[1].Value; break; } | |
} | |
catch { } | |
Thread.Sleep(100); | |
} | |
} while (deviceUrl == null && retry-- > 0); | |
} | |
//*/ | |
} | |
// ルータのデバイス情報のURLとれた | |
//Console.Error.WriteLine("devurl: " + deviceUrl); | |
if (deviceUrl == null) return null; | |
// 指定されたServiceIDのコントロールURLを取得する。 | |
string controlUrl = null; | |
{ | |
try | |
{ | |
XPathNavigator doc = new XPathDocument(deviceUrl).CreateNavigator(); | |
XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable); | |
nsm.AddNamespace("D", "urn:schemas-upnp-org:device-1-0"); | |
// 子に <serviceId>targetServiceID</serviceId> を持つ <controlURL> を探す | |
var targetServiceID = "urn:upnp-org:serviceId:" + targetConnServiceID; | |
var x = doc.SelectSingleNode("//D:serviceId[text()='" + targetServiceID + "']/../D:controlURL", nsm); | |
if (x != null) | |
{ | |
// URLの絶対化 | |
controlUrl = new Uri(new Uri(deviceUrl), x.InnerXml).ToString(); | |
} | |
} | |
catch (Exception) | |
{ | |
// リクエストに失敗, XML解釈に失敗, etc | |
} | |
} | |
// コントロールURLがとれた | |
//Console.Error.WriteLine("contUrl: " + controlUrl); | |
if (controlUrl == null) return null; | |
// コントロールURLに対して、GetExternalIPAddress | |
IPAddress myIp = null; | |
{ | |
byte[] request = Encoding.ASCII.GetBytes( | |
@"<?xml version=""1.0""?> | |
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/""> | |
<s:Body> | |
<u:GetExternalIPAddress xmlns:u=""urn:schemas-upnp-org:service:WANPPPConnection:1"" /> | |
</s:Body> | |
</s:Envelope>"); | |
try | |
{ | |
// SOAP。 | |
HttpWebRequest rq = (HttpWebRequest)HttpWebRequest.Create(controlUrl); | |
rq.Method = "POST"; | |
rq.Headers.Add("SOAPAction", "\"urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress\""); | |
rq.ContentType = "text/xml; charset=utf-8"; | |
rq.GetRequestStream().Write(request, 0, request.Length); | |
rq.GetRequestStream().Close(); | |
using (HttpWebResponse rs = (HttpWebResponse)rq.GetResponse()) | |
{ | |
XPathDocument doc = new XPathDocument(rs.GetResponseStream()); | |
XPathNavigator node = doc.CreateNavigator().SelectSingleNode("//NewExternalIPAddress"); | |
// IPアドレスは…… | |
if (node != null) | |
{ | |
myIp = IPAddress.Parse(node.InnerXml); | |
} | |
} | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
// IPアドレスが取れた | |
//Console.Error.WriteLine("ipAddr: " + myIp); | |
return myIp; | |
} | |
/// <summary> | |
/// ValueDomainのDDNS更新URLを使って、DNSレコードを書き換える | |
/// </summary> | |
/// <param name="address">更新後IPアドレス</param> | |
/// <param name="targetDomain">ドメイン</param> | |
/// <param name="targetHost">ホスト名</param> | |
/// <param name="password">更新パスワード</param> | |
/// <returns>成功したらnull。失敗したら起きた例外</returns> | |
static Exception UpdateValueDomainDDNS(IPAddress address, string targetDomain, string targetHost, string password) | |
{ | |
using (WebClient wc = new WebClient()) | |
{ | |
try | |
{ | |
// ValueDomainへリクエスト | |
string requestUrl = string.Format("http://dyn.value-domain.com/cgi-bin/dyn.fcg?d={0}&h={1}&p={2}&i={3}", | |
targetDomain, targetHost, updatePassword, address.ToString()); | |
string resultString = wc.DownloadString(requestUrl); | |
bool succeeded = resultString.IndexOf("status=0") >= 0; // リクエスト成功? | |
if (!succeeded) | |
{ | |
throw new ApplicationException("Update error: " + resultString); | |
} | |
} | |
catch (Exception ex) | |
{ | |
return ex; | |
} | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment