フルサービスリゾルバをなんとなく可視化してみた

#はじめに
こちらは<a href="https://adventar.org/calendars/5402"> SLP KBIT Advent Calendar 2020 </a href>24日目の記事です．
他のサークルメンバーが面白い記事を多く書いているのでぜひご覧ください．

今日は12/24，もう少しでお正月なシーズンですね，うん．

#作ったもの
　今回作ったものはタイトルにもある通り「フルサービスリゾルバの可視化」です．見た目は写真のようになりました．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/4b7c91ce-5f67-ff76-68d2-62bb78f3a386.png)
いきなりフルサービスリゾルバと言われても…という方がいると思うので，そちらから説明していきます．

#フルサービスリゾルバとは
　以下のサイトにこのように書かれています．
http://e-words.jp/w/%E3%83%AA%E3%82%BE%E3%83%AB%E3%83%90.html
>単体でDNSによる再帰的な問い合わせを繰り返し、自力ですべてのドメイン名の名前解決を行なうことができるリゾルバ

例えば，hoge.co.jpの場合，まずルートサーバに聞きに行く，するとjpサーバの在処を教えられるのでそこにアクセスする．jpサーバにhoge.co.jpを聞きにいくと，co.jpサーバの在処を教えられる．そして，co.jpに聞きに行くとhoge.co.jpサーバの在処を教えられる．
この一連の動作を一人でやってくれるものになります．~~わかりにくい．~~

#作ったものを動かしてみる
以下，hoge.comで検索した例です．

まず，ルートサーバに聞きに行きます．ちなみにルートサーバは世界に13台しかありません．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/ec007219-7973-4334-e90b-fa5007699995.png" width="70%">
ルートサーバは知らなかったみたいですが，知っていると思われるサーバを教えてくれました．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/482f79dd-5aa3-c16c-9247-b9dc9f68fd22.png" width="70%">
そこで，ルートサーバから教えてもらったcomサーバに聞きに行きます．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/41700f12-d98f-c598-6bdb-f6511f0d6675.png" width="70%">
comサーバはhoge.comを知っているみたいで，場所を教えてくれました．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/f4d30a57-d173-f215-b601-caf53c6fb2fd.png" width="70%">
最終的に発見できました．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/41f61e1e-7b64-1ff8-234b-1fe6838a5604.png" width="70%">
以上がフルサービスリゾルバの流れとなります．

ちなみにこれの後に「hmhm.example.com」を検索すると，最終的に以下の写真のようになります．
新しく出てきたサーバのみ追加するようになっています．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309106/85c4c1f7-c6b4-de7e-1dda-162c5dc70e51.png" width="70%
">

#コード内容

##HTML
```HTML
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>フルサービスリゾルバ可視化</title>
	<link rel="stylesheet" type="text/css" href="css/style.css">
	<script src="https://cdn.jsdelivr.net/npm/leader-line@1.0.1/leader-line.min.js"></script>
</head>
<body>
<div>
	<span>ユーザ</span>
	<img src="img/usr.png">
	<input id="domain" type="text" value="hoge.com">
	<button onclick="surfIP()">検索</button>
</div>
<br>
<div>
	<span>フルサービスリゾルバ</span>
	<div style="text-align: center;"><img src="img/resolvor.png" id="cash"></div>
</div>
<br>
<div>
	<span>ルートサーバ</span>
	<div style="text-align: center;"><img src="img/saba.png" id="root"></div>
</div>

<div class="aroundLine" id="top">
	<span>トップレベルドメインサーバ</span>
</div>
<br>
<div class="aroundLine" id="second">
	<span>セカンドレベルドメインサーバ</span>
</div>
<br>
<div class="aroundLine" id="third">
	<span>サードレベルドメインサーバ</span>
<script type="text/javascript" src="js/line.js"></script>
</body>
</html>
```
　特筆することはそんなにないです．

##css

```css
.aroundLine{
	border : solid 3px ;
	display: flex;
}
```
　サーバの図を横並びに上手く並べる方法がなかったのですが，flexを使うことで何とかなりました．

##JavaScript

```javascript
var level = ["top", "second", "third"]; 
var requestDomain = new Array;
var responseDomain = new Array;
var timer = 500;

requestDomain['root'] = new LeaderLine(
  		document.getElementById('cash'),
  		document.getElementById('root'), {hide: true});

responseDomain['root'] = new LeaderLine(
  		document.getElementById('root'),
  		document.getElementById('cash'), {hide: true});

//-- 検索時の動き
async function surfIP(){
	var domain = document.getElementById('domain').value;
	var levelDomain = domain.split('.');
	var tmpDomain;
	var length = levelDomain.length - 1;

	if(length > 2){
		alert("システム上無効なドメイン名です");//サードレベルドメインサーバ
		return;
	}
	
	//rootだけ別枠
	requestDomain['root'].show();
	await sleep(timer);
	alert(domain + "ってどこ？");
	requestDomain['root'].hide();
	responseDomain['root'].show();
	await sleep(timer);
	alert("私は知らないけど" + levelDomain[length] + "が知ってるよー");
	responseDomain['root'].hide();


	//以降繰り返し
	for (var i = 0; i <= length; i++) {
		if(i == 0) tmpDomain = levelDomain[length - i];
		else tmpDomain = levelDomain[length - i] + "." + tmpDomain;

		var cell = document.getElementsByClassName(level[i] + "Level");
		//console.log(cell.length);
		for (var j = 0; j < cell.length; j++) {
			if(tmpDomain == cell[j].innerHTML){
				j--;
				break;
			}
		}
		console.log(cell);
		if(cell.length == j){
			addServer(level[i] + "Level", tmpDomain, level[i]);
		}

		//-- LeaderLineの逐次実行
		requestDomain[tmpDomain].show();
		await sleep(timer);

		if(i == length){     // 最後なら関数終わり
			alert("発見!!!!!!");
			requestDomain[tmpDomain].hide();
			break;
		}

		alert(domain + "ってどこ？");
		requestDomain[tmpDomain].hide();
		responseDomain[tmpDomain].show();
		await sleep(timer);
		if(i == 0 && length == 2){     //ドメインレベルがthirdまであり，かつ，現在トップドメインにいるとき
			alert("私は知らないけど" + levelDomain[length - 1] + "." + tmpDomain + "が知ってるよー");
		}else{
			alert(levelDomain[0] + "." + tmpDomain + "は知ってるから教えるよー");
		}
		responseDomain[tmpDomain].hide();
		//console.log(length + "aa");
		//console.log(i + "ss");
	}

}

//-- サーバーがまだページ上にない時追加しておく
function addServer(htmlClass, domain, divID){
	var div = document.createElement('div');
	var span = document.createElement('span');
	var image = document.createElement('img');
	var br = document.createElement('BR');
	var level = document.getElementById(divID);

	
	image.src = "img/saba.png";
	span.classList.add(htmlClass);
	span.innerHTML = domain;
	div.id = domain;
	div.appendChild(image);
	div.appendChild(br);
	div.appendChild(span);
	level.appendChild(div);

	requestDomain[domain] = new LeaderLine(
  		document.getElementById('cash'),
  		document.getElementById(domain), {hide: true});

	responseDomain[domain] = new LeaderLine(
  		document.getElementById(domain),
  		document.getElementById('cash'), {hide: true});  		
}

//-- sleepないから作る
function sleep(waitMsec){
	return new Promise(function(resolve){
		setTimeout(function(){resolve()}, waitMsec);
	})
}
```

###LeaderLine
　なかなかに便利です．矢印を自由度高めで引けるので便利です．
https://anseki.github.io/leader-line/

###sleep
　javascriptにはsleep関数なるものがないので自分で作る必要があります．私はasync/awaitの機能を使って作りました．以下のサイトに他の方法も書いてあります．
https://www.sejuku.net/blog/24629

###その他
innerHTML使ったりgetElementByIDとかよく使われるやつですね．個人的にはgetElementsByClassNameが便利だなあと思いました．ほしい情報をまとめて取れるから楽でした．

#さいごに

　可視化というか，動いているだけ，みたいな感じになりました．おそらく詳しく見たら違う部分もあるとは思いますが，フルサービスリゾルバはこのような動きをします．これを見てわかる通り，世界に13台しかないルートサーバに大きな負担がかかることが分かると思います．そのため，ルートサーバの負担を減らすためにDNSキャッシュサーバに一時的にデータを保存しておき，毎回ルートサーバに聞きに行かずに済むようになっています．詳しいことは各々調べてください．
　最後まで読んでいただきありがとうございました．