😎

LwIPにおけるDNS Client処理

2024/06/25に公開

以前のこちらの記事で、LwIPの初期化~TCP通信に関してまとめたので、LwIPをDNS Clientとして動作させる方法に関してまとめようかと思います。

DNS

まず、DNSとはどんなものか説明しておきます。
DNSはDomain Name System の略で、ドメイン名とIPアドレスを紐づけて管理するシステムのことです。
ここでいうドメイン名というのはウェブブラウザで入力するURLに含まれているもので、
下記のようなURLがあるとしたとき、ドメイン名は「www.example.com」となります。

URL例: https://www.example.com/folder/No1
※URLにおいて先頭の「https:」は通信に使用するプロトコルを指し、「www.example.com/~ 」以降の記述に関してはファイルパスなどが記載されます(DNS clientとは直接かかわりはないので説明は省略します)。

インターネットにおいて、通信先のIPアドレスを知らなければ通信することができないので、DNSはこのドメイン名からIPアドレスを取得するために使用されます。
また、ドメイン名からIPアドレスを取得することを「名前解決」と呼びます。

DNSの仕組みに関しても簡単に説明します。

DNSの仕組み

DNSを使用して名前解決をする際は、ドメイン名を持っていてIPアドレスを知りたいデバイスがDNS Clientとなり、DNSサーバとなるデバイスへドメイン名と対応するIPアドレスを訪ねる形で行います。先ほど例として挙げたドメイン名「www.example.com」のIPアドレスを取得する場合を図で表すと下記のようになります。

上記の図でいうルートサーバとは、「.jp」「.com」などのドメイン名の一番右端の部分を管理するサーバ(DNSサーバ)がインターネット上のどこにあるかを認識するサーバです。
ただルートサーバは、ドメイン名が「~.jp」「〜.com」における「~」部分のドメイン名を管理するDNSサーバは把握していません。そこで、「.jp」「.com」を管理するDNSサーバに、「~」のDNSサーバに関して問い合わせます。
これを繰り返していくことでドメイン名から正確なIPアドレスを取得します。

追加するコンフィグ

DNS Clientを使用するための設定を行います。各設定状態を記述する、LwIPのコンフィグ用のヘッダファイル「lwipopt.h」に下記を追加します。

#define LWIP_DNS                        1

LwIPのDNS Client用API

次に、LwIPをDNS Clientとして動作させるAPIをここで説明します。
LwIPにおけるDNSに関してはこちらに説明がありますが、呼び出さなくても問題ないAPIも多いので、ここでは通常使用するもののみを説明します。

dns_gethostbyname

引数で指定したドメイン名の名前解決における問い合わせを開始します。
仕様はこちらです

項目1 項目2 説明
機能 引数で指定したドメイン名の名前解決における問い合わせを開始
引数 hostname 名前解決を行いたいドメイン名
addr 空のIPアドレスを示す変数のポインタ。LwIP側で持つDNSのキャッシュ情報に第一引数で指定したドメイン名のIPアドレスがあれば、ここに格納
found 名前解決が完了した際に呼ばれる関数のポインタ
callback_arg 「found」で指定した関数が呼ばれる際に第三引数で指定される変数のポインタ
返り値 エラーコード。正常終了したらERR_OK(= 0)を返す

DHCPでDNS ClientとなるデバイスのIPアドレスを取得した場合は、LwIPのDHCP Clientの内部処理で最初の問い合わせ先となるDNS serverのIPアドレスを設定してくれるので、このAPIを呼び出すだけでDNSの名前解決を開始できます。
また、指定したドメイン名のIPアドレスの取得完了した際は、第三引数で指定したコールバック関数が呼ばれます。
LwIPにおけるDNSの名前解決のシーケンスを下記に示します。

IPアドレス取得時に呼ばれるコールバック関数の仕様はこちらです。この仕様に沿った返り値と引数の関数を作成する必要があります。

typedef void(* dns_found_callback) (const char *name, const ip_addr_t *ipaddr, void *callback_arg)
項目1 項目2 説明
機能 ドメイン名の名前解決終了時に呼ばれる関数(コールバック関数)
引数 name 名前解決を行ったドメイン名
addr 取得したIPアドレス
callback_arg 「dns_gethostbyname」を呼び出した際の第四引数で指定したポインタ

第三引数は、「dns_gethostbyname」を呼び出した際の第四引数で指定したポインタが指定されます。ここに取得したIPアドレスを格納したい構造体や変数を指定しておくと良いでしょう。

dns_setserver

ドメイン名の最初の問い合わせ先となるDNSサーバのIPアドレスを設定します。
DHCPでDNS ClientとなるデバイスのIPアドレスを取得していない場合に、呼び出す必要があります。
仕様はこちらです。

項目1 項目2 説明
機能 ルートサーバのIPアドレスの設定
引数 numdns DNSサーバーのIPアドレスを格納する配列のインデックス
addr 設定したいDNSサーバのIPアドレス

第一引数に関しては、DNSサーバのIPアドレスを複数設定する必要がなければ0を指定すれば問題ないです。
第二引数で指定するDNSサーバのIPアドレスは、Googleが管理している無料のDNSサービス用のIPアドレス「8.8.8.8」あたりを指定すれば問題ないと思います。

サンプルソース

LwIPにおけるDNS Clientとして動作するサンプルを示します。
LwIPにおける初期化処理とDHCP Clientの処理に関しては、こちらの記事でまとめたので、説明は割愛します。

DNS Clientのサンプルソースコードは下記となります。dns_sample()関数を呼び出すことでドメイン名「httpbin.org」のIPアドレスを取得します。

#include "lwip/dns.h"


/* DNS server address example: 8.8.8.8 */
#define DNS_SERVER_ADDR1        (8)
#define DNS_SERVER_ADDR2        (8)
#define DNS_SERVER_ADDR3        (8)
#define DNS_SERVER_ADDR4        (8)

static ip_addr_t hostIpAddr;

void dns_get_address_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
    ip_addr_t* getHostIpAddr = (ip_addr_t*)callback_arg;
    if (getHostIpAddr != NULL)
    {
        // 「callback_arg」は「hostIpAddr」と同じ。取得したIPアドレスをグローバル変数に格納
        memcpy((void*)getHostIpAddr, (void*)ipaddr,sizeof(ip_addr_t));
    }
}


static void dns_client_start(const char *name)
{
    ip_addr_t ipAddr;
   
    // DHCPでIPアドレスを取得していない場合はdns_setserverを呼び出す必要がある
    //ip_addr_t dnsServerIpAddr;
    //IP_ADDR4(&dnsServerIpAddr,DNS_SERVER_ADDR1,DNS_SERVER_ADDR2,DNS_SERVER_ADDR3,DNS_SERVER_ADDR4);
    //dns_setserver(0,&dnsServerIpAddr);

    // DNSの名前解決を開始
    // IPアドレスが取得されたら、dns_get_address_callbackを呼び出す
    dns_gethostbyname(name, &ipAddr, dns_get_address_callback, (void*)&hostIpAddr);

    /* IPアドレスを取得するまで待つ */
    while(ip_addr_isany(&hostIpAddr))
    {
    	/* wait 500 ms */
    	osDelay(500);
    }
}

void dns_sample(struct netif *netif)
{
    /* DHCPによるIPアドレス取得 */
    /* netifはLwIP初期化時に設定したものを使う */
    while(!dhcp_supplied_address(netif))
    {
        osDelay(100);
    }

    /* DNS 名前解決開始 */
    dns_client_start("httpbin.org");
}

このサンプルにおいて名前解決を行うドメイン名「httpbin.org」は、HTTP リクエストのテストやデバッグに使用できるWeb サービスです。ウェブブラウザでこちらのリンクからアクセスできます。

参考文献

https://developer.mozilla.org/ja/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL
https://www.nic.ad.jp/ja/newsletter/No22/080.html
https://www.nongnu.org/lwip/2_1_x/group__dns.html#gaf66c5d8273f83cdc2cdd8911fb68d584

Discussion