Private dynamic IP - come, see, hide

image
Communication with DynDNS services literally from the first day of acquaintance did not work for me. A rake came across at every step: registering, downloading and launching a client, setting up a client or router - everywhere there were some minor nuances, shortcomings, imperfections or simply bugs, which led to the inoperability of the service. On top of everything, after a while, “these guys” suddenly cease to be white, fluffy and free - they start sending spam, once a month they demand to solve the captcha, or they are forced to do some other movements to prove that you are still alive. All this led to a general hostility to all services of this kind. And so the idea arose to create something of their own, and that necessarily "white and fluffy."

A lot of time has passed from the idea to the implementation. Mainly due to a misunderstanding of “what exactly do I need?”. I read articles at my leisure, kumekul, and gradually a list of basic requirements for a bicycle appeared in my head.

The main provisions.


Purpose : to find out the IP address of a remote computer (for example, a home computer).
Paranoia Level : Above Average! (that is, only trusted persons should know the IP address). This is precisely the main difference from similar services - I don’t want anyone to be able to get the address of my computer by simply driving something like “ping supercomp.dyndns.org” on the command line.
Mandatory conditions of "fluffiness" :
  1. Free of charge (do not forget that time is also money).
  2. Stability.
  3. The simplicity of a turnkey solution for the end user.

Based on the refinement to the first condition, the technology decided to use only those that I personally are more or less familiar with - Windows, c #, ASP.NET.
Under the influence of the article “ My Simple DynDNS Server ”, an attempt was made to write a small intermediary site. But, looking at the amazingly stable non-stability of free ASP.NET hosting, it was decided to abandon this idea and use free email services and cloud storage as intermediaries. By the way, it seemed from the mentioned article that the sound idea was taken with the idea of ​​“the ability to store IP addresses of all client interfaces”.
So somehow it turned out that this should be a regular Windows C-Sharp application.

The choice of "storage"


Under the repository is meant a certain place where our information will lie. This place should be protected from prying eyes, be easily accessible from anywhere and must meet three “fluffy” requirements.
In order not to strain much, it was decided to dwell on such options:
  • Computer file system (for example, a folder synchronized by some cloud client) - saving or reading problems does not cause any problems; all network work is on the cloud client.
  • Mail - letters are sent without problems, but you have to read through a third-party free library.
  • Cloud storage (meaning interaction with the cloud without installing a client) is quite feasible.

We will stop at the third point and consider possible options.
A preliminary survey of friends and acquaintances showed that most have nothing against Yandex Drive and Sky Drive. Therefore, they were initially considered as the main applicants. But after spending half a day in the “active search”, it turned out that not every cloud service provides a sane means of interaction. For example, the Sky Drive API has been impossible to use in desktop applications for some time, the Google Drive API cannot be figured out without a bottle, and with DropBox - I somehow did not find the SDK for Windows at all. The use of non-official or outdated “APIs” was not even considered, since there is no guarantee that they will work tomorrow. Maybe I was looking badly, or not looking there and not looking - I don’t know if anyone has any examples, I will be glad to help. The last highlight in the problem of choosing a cloud service was the fact
I did not dwell on any of these three types of storage / transfer. It was decided to make support for all three, and what specifically to use is left to the user's choice. Because the situations are different - for some the ports are closed and the mail does not work, for some people you can’t install cloud client programs, etc.

The general algorithm of the application.


The general algorithm of operation is simple as two pennies:
  1. Periodically save text messages with all the necessary information in the "repository"
  2. We periodically read messages, and show them in a convenient way.

Let's move on to implementing ideas in program code.

Getting an external address.

Everything is simple here. The "Internet" is full of all sorts of services that show your external address. If there are few existing ones, then creating a couple more dozen will not be difficult. Sample code for such a page on ASP.NET:
protected void Page_Load(object sender, EventArgs e)
{
	LabelIp.Text = HttpContext.Current.Request.UserHostAddress;
}

Let's get back to our application. Using the System.Net.WebClient class, download the page with this address into a string, parse it with a regular expression and get the information we need:
WebClient webClient = new WebClient();
string strExternalIp = webClient.DownloadString("http://checkip.dyndns.org/");
strExternalIp = (new Regex(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")).Matches(strExternalIp)[0].ToString();

Getting the properties of network interfaces.

The System.Net.NetworkInformation.NetworkInterface class and its static GetAllNetworkInterfaces () method, which returns an array of elements of the same type as NetworkInterface [], will help us in this. After going through this array, we can get from the IPInterfaceProperties object all the information we need - IP addresses, masks, gateways, dns servers, etc.:
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
// перебираем все сетевые интерфейсы
foreach (NetworkInterface nic in adapters)
{
	string strInterfaceName = nic.Name;	// наименование интерфейса
	string strPhysicalAddress = nic.GetPhysicalAddress().ToString(); //МАС - адрес
	string strAddr = string.Empty;
	// перебираем IP адреса
	IPInterfaceProperties properties = nic.GetIPProperties();
	foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses)
	{
		strAddr = unicast.Address.ToString() + " / " + unicast.IPv4Mask;
	}
	// перебираем днс-сервера
	foreach (IPAddress dnsAddress in properties.DnsAddresses)
	{
		strAddr = dnsAddress.ToString();
	}
	// перебираем шлюзы
	foreach (GatewayIPAddressInformation gatewayIpAddressInformation in properties.GatewayAddresses)
	{
		strAddr = gatewayIpAddressInformation.Address.ToString();
	}
}

Sending a text message to the "repository".

Having collected all the necessary information, we send it to the “repository" in the form of a regular text file (in the case of mail, just a message).
With ordinary files, everything is simple:
System.IO.File.WriteAllText("MyInterfaces.txt", strInterfaces);

With mail, too, everything is solved with a couple of lines of code (the method is easily located on the Internet). One possible variation:
MailMessage mail = new MailMessage
{
	From = new MailAddress(strMailAddress), // от кого
	Subject = strSubject,	// тема письма
	Body = strBody,			// тело письма
	IsBodyHtml = false
};
mail.To.Add(new MailAddress(Settings.Default.strMailTo)); // кому
SmtpClient client = new SmtpClient
{
	Host = strSmtpServer, // адрес SMTP сервера
	Port = nSmtpServerPort,	// порт SMTP сервера
	EnableSsl = isSmtpSsl,	// нужно ли испльзовать SSL
	Credentials = new NetworkCredential(strEmailUserName, strMailPassword), // логин пароль
	DeliveryMethod = SmtpDeliveryMethod.Network
};
client.Send(mail); // отправляем
mail.Dispose();

But with clouds a little more complicated, the general sense is to create the correct web request into which to push the transmitted text:
// strFilePath - имя и путь к файлу на сервере
HttpWebRequest web = (HttpWebRequest)WebRequest.Create("https://webdav.yandex.ru/" + strFilePath);
// указываем логин и пароль (дважды!!! в разных местах)
web.Credentials = new NetworkCredential("mail@yandex.ru", "password");
web.Headers.Add("Authorization: Basic " + Convert.ToBase64String(Encoding.Unicode.GetBytes("mail@yandex.ru" + ":" + "password")));
web.Accept = "*/*";
web.Method = "PUT";
web.ContentType = "application/binary";
web.ContentLength = buffer.Length;
using (Stream myReqStream = web.GetRequestStream())
{
	// strContent - текст передаваемого файла
	byte[] buffer = Encoding.UTF8.GetBytes(strContent); 
	myReqStream.Write(buffer, 0, buffer.Length);
	myReqStream.Flush();
}
HttpWebResponse resp = (HttpWebResponse)web.GetResponse();

Here I had to dance a bit with encodings, but using the “scientific poke” method, it was found that everything works fine with UTF8.

Reading messages from the repository

Regular files from a regular file system are read in one line. But we don’t need just one file, and its name may not be known in advance, therefore, we look at the entire contents of the folder, look for files by the specified mask and process them in turn:
// просмотр всех файлов из указанной директории по указанной маске
var files = Directory.EnumerateFiles("путь к папке", "*.txt");
strFileNames = files as string[] ?? files.ToArray();
foreach (string strFileName in strFileNames)
{
	string message = File.ReadAllText(strFileName); // читаем содержимое файла
	// что-то делаем с прочитанным
}

I had to tinker with reading mail. The code was sharpened by google-mail, therefore, incorrect operation on other mailers is possible. It was google-mail that led to the use of an IMAP server (currently the hotmail does not support this protocol). Many advised using a pseudo-free library (I will not give the name), which periodically instead of the body of the letter returned its advertising. But this directly violates the “second fluffy demand” - stability, and if you pay, then the “first fluffy demand” - free of charge. Therefore, I chose a completely free and completely working library in which there is work with IMAP servers - " MailSystem.NET ". Examples of use can be found on the project page, here I will give a small piece of code to receive the letter:
Imap4Client imap = new Imap4Client();
imap.ConnectSsl("imap.gmail.com", 993);	// подключаемся
imap.Login("mail@google.com", "password");// авторизуемся
Mailbox inbox = imap.SelectMailbox("inbox");// получаем папку входящих
int[] nIdsUnread = inbox.Search("UNSEEN");	// получаем только непрочитанные
int nUnreadCount = nIdsUnread.Length;	// узнаем количество непрочитанных
for (int i = 0; i < nUnreadCount; i++)
{
	int idx = nIdsUnread[i]; // получаем индекс письма в папке входящих
	// получаем текст сообщения
    Message message = inbox.Fetch.MessageObject(idx);
	// message.Subject - содержит тему письма
	// message.BodyText.Text - содержит текст письма
	// обрабатываем полученную информацию
}

So you can read the letter - only ten lines of code, but they drag five libraries (DLLs) into the program folder along with them, and then you have to drag them everywhere with you.

Reading files from cloud storage is even easier than sending them there:
// strFilePath - имя и путь к файлу на сервере
HttpWebRequest web = (HttpWebRequest)WebRequest.Create("https://webdav.yandex.ru/" + strFilePath);
// указываем логин и пароль
web.Credentials = new NetworkCredential("mail@yandex.ru", "password");
web.Headers.Add("Authorization: Basic " + Convert.ToBase64String(Encoding.Unicode.GetBytes("mail@yandex.ru" + ":" + "password")));
web.Accept = "*/*";
web.Method = "GET";
HttpWebResponse resp = (HttpWebResponse)web.GetResponse();
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
	string text = sr.ReadToEnd();
	// text - теперь содержит в себе текстовое содержимое файла
}

But this example will read only one file, and we need to read all the files from the specified directory. This problem is solved by a preliminary request for a list of files. The server will return the XML file to us, and go through the contents of the tags we get a list of files:
// strPath - путь к папке на сервере
HttpWebRequest web = (HttpWebRequest)WebRequest.Create("https://webdav.yandex.ru/" + strPath);
// указываем логин и пароль
web.Credentials = new NetworkCredential("mail@yandex.ru", "password");
web.Headers.Add("Authorization: Basic " + Convert.ToBase64String(Encoding.Unicode.GetBytes("mail@yandex.ru" + ":" + "password")));
web.Accept = "*/*";
web.Headers.Add("Depth: 1");
web.Method = "PROPFIND";
List retValue = new List(); // в этот список попадут все файлы из указанной паки
HttpWebResponse resp = (HttpWebResponse)web.GetResponse();
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
	// сервер возвращает XML файл. Разбираем его содержимое:
	XmlDocument xmlDoc = new XmlDocument();
	xmlDoc.LoadXml(sr.ReadToEnd());
	XmlNodeList displaynames = xmlDoc.GetElementsByTagName("d:displayname");
	int nCount = displaynames.Count;
	for (int i = 1; i < nCount; i++)
	{
		retValue.Add(displaynames[i].InnerText);
	}
}

DNS

After receiving all the information from the repository, the question arises - what to do next? And there are not many options:
  1. Show user everything received in a convenient way
  2. Arrange access to remote computers by name

With the first, everything is simple - a regular list and a couple of columns. But the second is more difficult. There are two ways to implement it:
  1. Editing the file% windir% \ system32 \ drivers \ etc \ hosts
  2. Create your local DNS server

The first is implemented quite simply, the hosts file is an ordinary text file that can be easily read / changed / saved, the main thing is to have rights to it. And the ordinary user does not have them, therefore, we increase the “Windows account management level” in our application by setting the value for requestedExecutionLevel = requireAdministrator in the app.manifest file. Read more about this here . We work with the host file like this:
//открываем файл хостов
string strHosts = File.ReadAllText(Environment.SystemDirectory + "\\drivers\\etc\\hosts");
string[] linesHostsOld = Regex.Split(strHosts, "\r\n|\r|\n"); // разбиваем на строки
StringBuilder sbHostsNew = new StringBuilder();
// обрабатываем все строки
foreach (string lineHosts in linesHostsOld)
{
    sbHostsNew.AppendLine(lineHosts);
}
// добавляем в конец текущие значения хостов
sbHostsNew.AppendLine("127.0.0.1  hello.world.com");
// сохраняем файл хостов
File.WriteAllText(Environment.SystemDirectory + "\\drivers\\etc\\hosts", sbHostsNew.ToString());

The second option I could not test well, since so far everyone is completely satisfied with the efficiency of the first method. The DNS server is implemented using the third-party library “ ARSoft.Tools.Net ”. Strongly not wise, and for these examples we do our functions, something like this:
DnsServer _server = new DnsServer(IPAddress.Any, 10, 10, ProcessQuery);
_server.Start(); // запуск сервера
// запрос адреса у DNS сервера
private static DnsMessageBase ProcessQuery(
	DnsMessageBase message, 
	IPAddress clientAddress, 
	ProtocolType protocol)
{
	message.IsQuery = false;
	DnsMessage query = message as DnsMessage;
	if (query != null)
	{
		if (query.Questions.Count == 1)
		{
			if (query.Questions[0].RecordType == RecordType.A)
			{
				if (query.Questions[0].Name.Equals("hello.world.com", StringComparison.InvariantCultureIgnoreCase))
				{
					IPAddress ip;
					if (IPAddress.TryParse("127.0.0.1", out ip))
					{
						query.ReturnCode = ReturnCode.NoError;
						DnsRecordBase rec = new ARecord(strHostName, 0, ip);
						query.AnswerRecords.Add(rec);
						return message;
					}
				}
			}
		}
	}
	message.ReturnCode = ReturnCode.ServerFailure;
	return message;
} 

Ready application.


Putting together all of the above, and adding a call to the necessary procedures on a timer, you get a kind of conceived program. It remains only to finalize everything with a file, bring it into a divine form and you can show it to people.
All settings (and it turned out quite a lot) the application stores in the% PROGRAM_NAME% .exe.config file and the file itself lies somewhere in this area:% USERPROFILE% \ AppData \ Local \% PROGRAM_NAME% \ ***. This is implemented using the standard capabilities of Properties.Settings.Default . Passwords are stored there, but in encrypted form. Encryption is done using DPAPI (there is an article and a question on the topic on the topic ).
I will not describe in detail the work with the form settings, with encryption, with timers, with parallel processes and everything else that does not concern the original topic. Who cares - you can always see the source code.

Appearance of the resulting bike:


Four screenshots of the program





At the first start, you will need to carefully configure all the necessary parameters.
In the minimum version: on the first computer (with a dynamic address) you will need to configure the “interfaces”, and on the second computer (on which we need to know the dynamic address) you will need to carefully configure the “hosts”.

Development plans.


  • Increase supported cloud storage.
  • Increase Supported Email Protocols
  • Encryption of transmitted information.


The source code of the project and the program itself has so far been uploaded to Yandex.Disk.
Sources can be downloaded here: http://yadi.sk/d/iZNy9wA28E0-E The
binary files are here: http://yadi.sk/d/kYpZIqdn8E-ui

That's all. Thanks for attention.

Also popular now: