Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 14171] ファイルの概要

このコミットは、Go言語の標準ライブラリである net パッケージに、DNSのネームサーバー (NS) レコードをルックアップするための新しい関数 LookupNS(domain string) を追加するものです。これにより、指定されたドメイン名に関連付けられたネームサーバー情報をプログラムから取得できるようになります。

コミット

commit a5b0c67d5f7d7957ee5fae8a2980f621d95ab719
Author: Stephen McQuay <stephen@mcquay.me>
Date:   Thu Oct 18 15:39:04 2012 +0900

    net: add LookupNS(domain string)
    Fixes #4224.
    
    R=golang-dev, dave, minux.ma, mikioh.mikioh, alex.brainman, rsc, herbert.fischer
    CC=golang-dev
    https://golang.org/cl/6675043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a5b0c67d5f7d7957ee5fae8a2980f621d95ab719

元コミット内容

net: add LookupNS(domain string) Fixes #4224.

このコミットは、Go言語の net パッケージに LookupNS 関数を追加します。これは、Issue #4224 を修正するためのものです。

変更の背景

この変更の背景には、Go言語の net パッケージが提供するDNSルックアップ機能の拡充があります。既存の net パッケージには、Aレコード (IPアドレス)、MXレコード (メールエクスチェンジャー)、TXTレコード (テキスト情報) などのルックアップ機能は存在していましたが、ネームサーバー (NS) レコードを直接ルックアップする機能が欠けていました。

Issue #4224 は、この欠如を指摘し、NSレコードのルックアップ機能の必要性を提起したものです。NSレコードは、ドメインのDNSゾーン情報を管理する権威ネームサーバーを特定するために不可欠な情報であり、ドメインの委任関係を追跡したり、特定のドメインのDNS設定を検証したりする際に利用されます。

このコミットは、この不足を解消し、GoプログラムからNSレコード情報を簡単に取得できるようにすることで、ネットワーク関連のアプリケーション開発における柔軟性と機能性を向上させることを目的としています。

前提知識の解説

DNS (Domain Name System)

DNSは、インターネット上のコンピュータやサービスを識別するための階層的な分散型命名システムです。人間が覚えやすいドメイン名 (例: example.com) を、コンピュータが理解できるIPアドレス (例: 192.0.2.1) に変換する役割を担っています。

DNSレコードタイプ

DNSには様々なレコードタイプがあり、それぞれ異なる種類の情報を提供します。

  • Aレコード (Address Record): ホスト名とIPv4アドレスのマッピング。
  • AAAAレコード (IPv6 Address Record): ホスト名とIPv6アドレスのマッピング。
  • MXレコード (Mail Exchange Record): ドメインのメールサーバーを指定。
  • TXTレコード (Text Record): ドメインに関連付けられた任意のテキスト情報。SPF (Sender Policy Framework) や DKIM (DomainKeys Identified Mail) などで利用されます。
  • NSレコード (Name Server Record): ドメインの権威ネームサーバーを指定します。あるドメインのDNS情報をどこで管理しているかを示す重要なレコードです。例えば、example.com のNSレコードは、ns1.example.comns2.example.com のようなネームサーバーを指し、これらのネームサーバーが example.com のAレコードやMXレコードなどの詳細な情報を持っています。

Go言語の net パッケージ

Go言語の net パッケージは、ネットワークI/Oプリミティブを提供します。これには、TCP/IP、UDP、IP、Unixドメインソケットなどのネットワークプロトコルを扱うための機能や、DNSルックアップ機能が含まれます。net パッケージは、クロスプラットフォームで動作するように設計されており、各OSのネイティブなDNS解決メカニズムを抽象化して提供します。

プラットフォームごとのDNS解決

DNS解決の具体的な実装は、オペレーティングシステムによって異なります。

  • Unix系 (Linux, macOSなど): 通常、/etc/resolv.conf に設定されたDNSサーバーを利用し、Cライブラリの getaddrinfores_query などの関数を通じてDNSクエリを実行します。
  • Plan9: Plan9は独自のネットワークスタックとDNS解決メカニズムを持っています。queryDNS のような内部関数を通じてDNSクエリを実行します。
  • Windows: Windowsは、DnsQuery などのWin32 API関数を使用してDNSクエリを実行します。

このコミットでは、これらのプラットフォームごとの違いを吸収し、統一された LookupNS インターフェースを提供するために、各プラットフォーム向けの lookupNS 実装が追加されています。

技術的詳細

このコミットは、Go言語の net パッケージにNSレコードのルックアップ機能を追加するために、以下の主要な変更を導入しています。

  1. NS 構造体の定義: src/pkg/net/dnsclient.go に、DNS NSレコードを表す新しい構造体 NS が定義されました。

    type NS struct {
        Host string
    }
    

    この構造体は、ネームサーバーのホスト名 (Host) を保持します。

  2. LookupNS 関数の追加: src/pkg/net/lookup.go に、公開APIとして LookupNS 関数が追加されました。

    func LookupNS(name string) (ns []*NS, err error) {
        return lookupNS(name)
    }
    

    この関数は、指定されたドメイン名 (name) のNSレコードをルックアップし、NS 構造体のスライスとエラーを返します。実際のルックアップ処理は、内部関数 lookupNS に委譲されます。

  3. プラットフォームごとの lookupNS 実装: lookupNS 関数は、Goのビルドタグ (_plan9, _unix, _windows) を利用して、各オペレーティングシステムに特化した実装が提供されます。

    • src/pkg/net/lookup_plan9.go: Plan9システム向けの lookupNS 実装が追加されました。これは、内部の queryDNS 関数を使用してDNSクエリを実行し、結果をパースして NS 構造体のスライスを生成します。

    • src/pkg/net/lookup_unix.go: Unix系システム向けの lookupNS 実装が追加されました。これは、既存の lookup 関数と dnsTypeNS を利用してNSレコードをクエリし、結果を dnsRR_NS 型にキャストして NS 構造体に変換します。

    • src/pkg/net/lookup_windows.go: Windowsシステム向けの lookupNS 実装が追加されました。これは、Windows APIの syscall.DnsQuery 関数を DNS_TYPE_NS タイプで呼び出し、返されたDNSレコードリストを処理して NS 構造体のスライスを構築します。特に、syscall.UTF16ToString を使用してUTF-16エンコードされたホスト名をGoの文字列に変換する処理が含まれています。

  4. テストケースの追加: src/pkg/net/lookup_test.go に、LookupNS 関数の動作を検証するためのテストケース TestGmailNS が追加されました。このテストは、gmail.com のNSレコードをルックアップし、結果が空でないことを確認します。testing.Short()*testExternal フラグを使用して、外部ネットワークへのアクセスを伴うテストの実行を制御しています。

これらの変更により、Goの net パッケージは、クロスプラットフォームでNSレコードのルックアップをサポートするようになり、開発者はOSの違いを意識することなくNSレコード情報を取得できるようになりました。

コアとなるコードの変更箇所

このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。

  • src/pkg/net/dnsclient.go:

    • NS 構造体が新しく定義されました。これは、DNS NSレコードのホスト名を保持します。
  • src/pkg/net/lookup.go:

    • LookupNS(name string) 関数が追加されました。これは、外部から呼び出されるNSレコードルックアップの公開インターフェースです。内部的にはプラットフォーム固有の lookupNS 関数を呼び出します。
  • src/pkg/net/lookup_plan9.go:

    • Plan9環境向けの lookupNS 関数が実装されました。queryDNS を使用してNSレコードを問い合わせ、結果をパースします。
  • src/pkg/net/lookup_test.go:

    • TestGmailNS という新しいテスト関数が追加されました。これは、LookupNS 関数が正しく動作するかどうかを検証するために、gmail.com のNSレコードをルックアップします。
  • src/pkg/net/lookup_unix.go:

    • Unix系環境向けの lookupNS 関数が実装されました。既存の lookup 関数と dnsTypeNS を利用してNSレコードを取得します。
  • src/pkg/net/lookup_windows.go:

    • Windows環境向けの lookupNS 関数が実装されました。syscall.DnsQuery を使用してNSレコードを問い合わせ、結果を NS 構造体に変換します。

コアとなるコードの解説

src/pkg/net/dnsclient.go の変更

// An NS represents a single DNS NS record.
type NS struct {
	Host string
}

このコードは、DNSのNSレコードを表す NS 構造体を定義しています。Host フィールドは、ネームサーバーのホスト名(例: ns1.example.com)を文字列として保持します。これは、LookupNS 関数が返す結果の型となります。

src/pkg/net/lookup.go の変更

// LookupNS returns the DNS NS records for the given domain name.
func LookupNS(name string) (ns []*NS, err error) {
	return lookupNS(name)
}

LookupNS は、net パッケージの公開APIとして追加された関数です。ユーザーが指定したドメイン名 (name) に対応するNSレコードを検索し、NS 構造体のスライスとエラーを返します。実際のDNSクエリ処理は、プラットフォーム固有の内部関数 lookupNS に委譲されています。この設計により、ユーザーはOSの違いを意識することなくNSレコードをルックアップできます。

src/pkg/net/lookup_plan9.go の変更

func lookupNS(name string) (ns []*NS, err error) {
	lines, err := queryDNS(name, "ns")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 4 {
			continue
		}
		ns = append(ns, &NS{f[3]})
	}
	return
}

Plan9システム向けの lookupNS 実装です。queryDNS 関数(Plan9固有のDNSクエリメカニズム)を呼び出して、指定されたドメイン名とレコードタイプ "ns" でDNSクエリを実行します。返された各行を getFields でフィールドに分割し、4番目のフィールド(インデックス3)がネームサーバーのホスト名であると仮定して NS 構造体を作成し、結果のスライスに追加します。

src/pkg/net/lookup_unix.go の変更

func lookupNS(name string) (ns []*NS, err error) {
	_, records, err := lookup(name, dnsTypeNS)
	if err != nil {
		return
	}
	ns = make([]*NS, len(records))
	for i, r := range records {
		r := r.(*dnsRR_NS) // dnsRR_NSはNSレコードの内部表現
		ns[i] = &NS{r.Ns}
	}
	return
}

Unix系システム向けの lookupNS 実装です。既存の汎用DNSルックアップ関数 lookupdnsTypeNS (NSレコードタイプを示す定数) と共に呼び出します。返されたレコードのスライスをイテレートし、各レコードを *dnsRR_NS 型に型アサーション(キャスト)します。dnsRR_NS 構造体の Ns フィールド(ネームサーバーのホスト名)を抽出し、新しい NS 構造体を作成して結果のスライスに格納します。

src/pkg/net/lookup_windows.go の変更

func lookupNS(name string) (ns []*NS, err error) {
	var r *syscall.DNSRecord
	e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
	if e != nil {
		return nil, os.NewSyscallError("LookupNS", e)
	}
	defer syscall.DnsRecordListFree(r, 1)
	ns = make([]*NS, 0, 10)
	for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next {
		v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
		ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
	}
	return ns, nil
}

Windowsシステム向けの lookupNS 実装です。syscall.DnsQuery Windows API関数を呼び出してNSレコードをクエリします。エラーが発生した場合は os.NewSyscallError でGoのエラーに変換します。defer syscall.DnsRecordListFree を使用して、取得したDNSレコードリストのメモリを解放します。 返されたレコードリストをループ処理し、各レコードがNSタイプであることを確認します。DNSPTRData 型にポインタをキャストし、v.Host からホスト名を取得します。Windows APIはUTF-16を使用するため、syscall.UTF16ToString を用いてGoの文字列に変換し、末尾に . を追加して完全なドメイン名形式にします。

src/pkg/net/lookup_test.go の変更

func TestGmailNS(t *testing.T) {
	if testing.Short() || !*testExternal {
		t.Logf("skipping test to avoid external network")
		return
	}
	ns, err := LookupNS("gmail.com")
	if err != nil {
		t.Errorf("failed: %s", err)
	}
	if len(ns) == 0 {
		t.Errorf("no results")
	}
}

LookupNS 関数の動作を検証するためのテストケースです。testing.Short() フラグが設定されている場合や、testExternal フラグがfalseの場合(外部ネットワークへのアクセスを避けるため)、テストはスキップされます。テストでは gmail.com のNSレコードをルックアップし、エラーが発生しないこと、および結果が空でないことを確認します。これにより、LookupNS 関数が期待通りに動作することが保証されます。

関連リンク

  • Go Gerrit Change-ID: https://golang.org/cl/6675043
  • Go Issue #4224: (このコミットメッセージからは直接リンクされていませんが、Fixes #4224 とあるため、GoのIssueトラッカーで検索すると詳細が見つかる可能性があります。)

参考にした情報源リンク

  • 特になし (コミットメッセージとコード差分から直接情報を抽出しました)