[インデックス 10204] ファイルの概要
このコミットは、Go言語の標準ライブラリ net
パッケージにおいて、Windows環境での LookupTXT
関数の実装を追加するものです。これにより、Windows上でもDNSのTXTレコードの問い合わせが可能になります。
コミット
commit b43cf81c8c6a4809e89f7e470996b73d035a88e5
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Wed Nov 2 17:11:40 2011 +1100
net: implement LookupTXT for windows
R=rsc
CC=golang-dev
https://golang.org/cl/5318056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b43cf81c8c6a4809e89f7e470996b73d035a88e5
元コミット内容
このコミットは、Go言語の net
パッケージにおけるWindows固有のDNSルックアップ処理に、TXTレコードの問い合わせ機能 LookupTXT
を追加します。これまでのWindows版 LookupTXT
は「未実装」というエラーを返していましたが、この変更によりWindows APIの DnsQuery
を利用して実際にTXTレコードを取得できるようになります。また、関連するテストコードの修正と、Windowsシステムコールで使用する構造体 DNSTXTData
の定義が追加されています。
変更の背景
Go言語の net
パッケージは、ネットワーク関連の基本的な機能を提供しており、DNSルックアップはその重要な一部です。しかし、コミット当時のWindows環境では、TXTレコードの問い合わせ機能である LookupTXT
が実装されていませんでした。これは、GoアプリケーションがWindows上で動作する際に、TXTレコードに依存する機能(例えば、SPFレコードの検証やドメイン認証など)を利用できないという制約を意味していました。
このコミットは、その制約を解消し、Go言語が提供するネットワーク機能のクロスプラットフォーム互換性を向上させることを目的としています。WindowsのネイティブなDNS解決APIを活用することで、GoプログラムがWindows環境でも他のOSと同様にTXTレコードを扱えるようにすることが背景にあります。
前提知識の解説
DNS (Domain Name System)
DNSは、インターネット上のドメイン名とIPアドレスを対応させる分散型のデータベースシステムです。ユーザーがWebサイトのドメイン名(例: example.com
)を入力すると、DNSがそのドメイン名に対応するIPアドレス(例: 192.0.2.1
)を解決し、ブラウザがそのIPアドレスに接続できるようにします。
DNSレコードの種類
DNSには様々な種類のレコードがあり、それぞれ異なる情報を提供します。
- Aレコード (Address Record): ドメイン名とIPv4アドレスを対応付けます。
- AAAAレコード (IPv6 Address Record): ドメイン名とIPv6アドレスを対応付けます。
- CNAMEレコード (Canonical Name Record): あるドメイン名を別のドメイン名にエイリアスします。
- MXレコード (Mail Exchange Record): ドメインのメールサーバーを指定します。
- SRVレコード (Service Record): 特定のサービス(例: SIP、XMPP)が利用するサーバーとポート番号を指定します。
- TXTレコード (Text Record): ドメインに関連付けられた任意のテキスト情報を格納します。これは、SPF (Sender Policy Framework) や DKIM (DomainKeys Identified Mail) のようなメール認証、ドメイン所有権の確認、その他のアプリケーション固有のデータなど、多岐にわたる用途で利用されます。
Windows DNS API (DnsQuery
)
Windowsオペレーティングシステムは、DNSクエリを実行するためのネイティブなAPIを提供しています。その中心となるのが DnsQuery
関数です。この関数は、指定されたドメイン名とレコードタイプに基づいてDNSサーバーに問い合わせを行い、結果を DNSRecord
構造体のリストとして返します。
DnsQuery
: DNSクエリを実行するための主要な関数。name
: 問い合わせるドメイン名。wType
: 問い合わせるDNSレコードのタイプ(例:DNS_TYPE_TEXT
)。Options
: クエリのオプション。pExtra
: 予約済み。ppQueryResults
: クエリ結果のレコードリストへのポインタ。pReserved
: 予約済み。
DNS_TYPE_TEXT
: TXTレコードを問い合わせる際にDnsQuery
に渡すレコードタイプ。DNSRecord
構造体:DnsQuery
の結果として返されるDNSレコードの情報を格納する構造体。レコードのタイプに応じて、Data
メンバーが異なるデータ構造を指します。DNSTXTData
構造体: TXTレコードのデータ部分を表現する構造体。複数のテキスト文字列を格納できます。syscall.UTF16ToString
: Go言語のsyscall
パッケージで提供される関数で、Windows APIが返すUTF-16エンコードされた文字列(*uint16
の配列)をGoのUTF-8文字列に変換するために使用されます。Windowsの文字列は通常UTF-16で表現されるため、Goで扱う際にはこの変換が必要です。
Go言語の syscall
パッケージ
Go言語の syscall
パッケージは、オペレーティングシステムが提供する低レベルなシステムコールやAPIにアクセスするための機能を提供します。Windowsの場合、このパッケージを通じて DnsQuery
のようなWin32 APIを呼び出すことができます。
技術的詳細
このコミットの主要な技術的ポイントは、WindowsのネイティブDNS APIである DnsQuery
をGo言語から呼び出し、TXTレコードのデータを適切にパースしてGoの文字列スライスとして返す点にあります。
LookupTXT
関数の実装:syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
を呼び出し、指定されたドメイン名 (name
) のTXTレコード (DNS_TYPE_TEXT
) を問い合わせます。DnsQuery
の戻り値e
が0でない場合(エラーが発生した場合)、os.NewSyscallError
を使用してエラーを生成し返します。defer syscall.DnsRecordListFree(r, 1)
を使用して、取得したDNSレコードリストのメモリを解放します。これは、Windows APIで取得したリソースを適切にクリーンアップするために非常に重要です。
- TXTレコードのデータ抽出と変換:
DnsQuery
が成功した場合、r
にはDNSRecord
構造体へのポインタが格納されます。TXTレコードの場合、r.Type
はsyscall.DNS_TYPE_TEXT
となります。r.Data
メンバーは、レコードタイプに応じたデータ構造を指します。TXTレコードの場合、これはDNSTXTData
構造体へのポインタとして解釈されます。d := (*syscall.DNSTXTData)(unsafe.Pointer(&r.Data[0]))
の行で、r.Data
の先頭アドレスをDNSTXTData
型のポインタにキャストしています。unsafe.Pointer
は、Goの型安全性を一時的にバイパスして、任意の型へのポインタ変換を可能にするものです。DNSTXTData
構造体にはStringCount
(文字列の数)とStringArray
(文字列へのポインタの配列)が含まれています。for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount]
のループで、StringArray
内の各文字列ポインタをイテレートします。(*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))
は、d.StringArray[0]
のアドレスを*uint16
の配列(最大1024個の要素を持つ)へのポインタとして解釈しています。これは、StringArray
が可変長の文字列ポインタを保持する可能性があるため、十分なサイズの配列として扱うためのテクニックです。[:d.StringCount]
は、実際に存在する文字列の数 (d.StringCount
) だけスライスを作成し、ループで処理します。
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
の行で、各TXT文字列(v
は*uint16
型のポインタ)をGoの文字列に変換しています。(*[1 << 20]uint16)(unsafe.Pointer(v))
は、v
が指すUTF-16文字列を、最大1MBのUTF-16文字の配列へのポインタとして解釈しています。これは、文字列の長さを事前に知ることができないため、十分なバッファサイズを確保するための一般的なパターンです。syscall.UTF16ToString
は、このUTF-16バイト列をGoのUTF-8文字列に変換します。
- 変換された文字列は
txt
スライスに追加されます。
ztypes_windows.go
へのDNSTXTData
構造体の追加:- Windows APIの
DnsQuery
が返すTXTレコードのデータ構造に対応するため、src/pkg/syscall/ztypes_windows.go
にDNSTXTData
構造体が追加されています。 StringCount uint16
: TXTレコードに含まれる文字列の数。StringArray [1]*uint16
: TXTレコードの各文字列へのポインタの配列。[1]
となっているのは、Goの構造体定義の制約上、少なくとも1つの要素が必要なためであり、実際にはStringCount
の数だけ要素が存在すると解釈されます。
- Windows APIの
エラーハンドリングの統一
コミットでは、LookupCNAME
, LookupSRV
, LookupMX
, LookupAddr
の各関数で、DnsQuery
の戻り値 e
のチェックが if int(e) != 0
から if e != 0
に変更されています。これは、syscall.Errno
型が既に整数型として扱えるため、明示的な型変換 int(e)
が不要になったことを示唆しています。これにより、コードの簡潔性が向上しています。
コアとなるコードの変更箇所
src/pkg/net/lookup_test.go
--- a/src/pkg/net/lookup_test.go
+++ b/src/pkg/net/lookup_test.go
@@ -52,10 +52,6 @@ func TestGmailMX(t *testing.T) {
}
func TestGmailTXT(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Logf("LookupTXT is not implemented on Windows")
- return
- }
if testing.Short() || avoidMacFirewall {
t.Logf("skipping test to avoid external network")
return
TestGmailTXT
関数から、Windows環境でのLookupTXT
未実装に関するスキップロジックが削除されました。これは、LookupTXT
がWindowsで実装されたため、テストをスキップする必要がなくなったことを意味します。
src/pkg/net/lookup_windows.go
--- a/src/pkg/net/lookup_windows.go
+++ b/src/pkg/net/lookup_windows.go
@@ -5,7 +5,6 @@
package net
import (
- "errors"
"syscall"
"unsafe"
"os"
@@ -81,7 +80,7 @@ func LookupPort(network, service string) (port int, err error) {
func LookupCNAME(name string) (cname string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
- if int(e) != 0 {
+ if e != 0 {
return "", os.NewSyscallError("LookupCNAME", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
@@ -110,7 +109,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
}
var r *syscall.DNSRecord
e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
- if int(e) != 0 {
+ if e != 0 {
return "", nil, os.NewSyscallError("LookupSRV", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
@@ -126,7 +125,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
func LookupMX(name string) (mx []*MX, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
- if int(e) != 0 {
+ if e != 0 {
return nil, os.NewSyscallError("LookupMX", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
@@ -140,7 +139,21 @@ func LookupMX(name string) (mx []*MX, err error) {
}
func LookupTXT(name string) (txt []string, err error) {
- return nil, errors.New("net.LookupTXT is not implemented on Windows")
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
+ if e != 0 {
+ return nil, os.NewSyscallError("LookupTXT", int(e))
+ }
+ defer syscall.DnsRecordListFree(r, 1)
+ txt = make([]string, 0, 10)
+ if r != nil && r.Type == syscall.DNS_TYPE_TEXT {
+ d := (*syscall.DNSTXTData)(unsafe.Pointer(&r.Data[0]))
+ for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
+ s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
+ txt = append(txt, s)
+ }
+ }
+ return
}
func LookupAddr(addr string) (name []string, err error) {
@@ -150,7 +163,7 @@ func LookupAddr(addr string) (name []string, err error) {
}
var r *syscall.DNSRecord
e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
- if int(e) != 0 {
+ if e != 0 {
return nil, os.NewSyscallError("LookupAddr", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
errors
パッケージのインポートが削除されました。LookupCNAME
,LookupSRV
,LookupMX
,LookupAddr
関数内のエラーチェックif int(e) != 0
がif e != 0
に変更されました。LookupTXT
関数が完全に実装されました。以前は「未実装」というエラーを返していましたが、Windows APIのDnsQuery
を使用してTXTレコードを取得するロジックが追加されました。
src/pkg/syscall/ztypes_windows.go
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -530,6 +530,11 @@ type DNSMXData struct {
Pad uint16
}
+type DNSTXTData struct {
+ StringCount uint16
+ StringArray [1]*uint16
+}
+
type DNSRecord struct {
Next *DNSRecord
Name *uint16
DNSTXTData
構造体が追加されました。これは、Windows DNS APIが返すTXTレコードのデータ構造に対応するためのものです。
コアとなるコードの解説
このコミットの核となるのは、src/pkg/net/lookup_windows.go
内の LookupTXT
関数の実装です。
func LookupTXT(name string) (txt []string, err error) {
var r *syscall.DNSRecord
// DnsQuery を呼び出し、指定されたドメイン名 (name) の TXT レコード (DNS_TYPE_TEXT) を問い合わせる
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
if e != 0 {
// エラーが発生した場合、os.NewSyscallError を使用してエラーを生成し返す
return nil, os.NewSyscallError("LookupTXT", int(e))
}
// 取得した DNS レコードリストのメモリを解放する (defer で確実に実行される)
defer syscall.DnsRecordListFree(r, 1)
// 結果を格納する文字列スライスを初期化
txt = make([]string, 0, 10) // 10 は初期容量のヒント
// レコードが存在し、かつ TXT タイプである場合のみ処理
if r != nil && r.Type == syscall.DNS_TYPE_TEXT {
// DNSRecord の Data メンバーを DNSTXTData 構造体へのポインタとして解釈
d := (*syscall.DNSTXTData)(unsafe.Pointer(&r.Data[0]))
// DNSTXTData 内の StringArray をループ処理
// StringArray は *uint16 (UTF-16 文字列へのポインタ) の配列
// [1 << 10]*uint16 は、最大 1024 個の文字列ポインタを保持できる配列として扱うためのテクニック
// [:d.StringCount] で、実際に存在する文字列の数だけスライスを作成
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
// 各 UTF-16 文字列ポインタ (v) を Go の UTF-8 文字列に変換
// [1 << 20]uint16 は、最大 1MB の UTF-16 文字を保持できる配列として扱うためのテクニック
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
// 変換された文字列を結果スライスに追加
txt = append(txt, s)
}
}
return
}
このコードは、Windows環境でDNSのTXTレコードを問い合わせるための標準的な手順をGo言語で実装しています。DnsQuery
を使用してAPIを呼び出し、返された生データを unsafe.Pointer
を用いてGoの構造体にマッピングし、syscall.UTF16ToString
でUTF-16エンコードされた文字列をGoのUTF-8文字列に変換しています。これにより、GoアプリケーションはWindows上でもTXTレコードを透過的に利用できるようになります。
関連リンク
- Go言語の
net
パッケージ: https://pkg.go.dev/net - Go言語の
syscall
パッケージ: https://pkg.go.dev/syscall - Microsoft Learn - DnsQuery function: https://learn.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsquery_a
- Microsoft Learn - DNS_RECORD structure: https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_recorda
- Microsoft Learn - DNS_TXT_DATA structure: https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_txt_data
参考にした情報源リンク
- 上記のMicrosoft Learnのドキュメント
- Go言語のソースコードと関連するコミット履歴
- Go言語の
unsafe
パッケージに関するドキュメント(ポインタ操作の理解のため) - Go言語の
syscall
パッケージの利用例に関する一般的な情報 - DNSのTXTレコードに関する一般的な情報(RFCなど)
- Windows APIにおける文字列エンコーディング(UTF-16)に関する情報
- Go言語の
make
関数における容量の指定に関する情報 - Go言語の
defer
ステートメントに関する情報 - Go言語のテストにおける
t.Logf
やtesting.Short()
の利用に関する情報 - Go言語の
os.NewSyscallError
の利用に関する情報 - Go言語の
runtime.GOOS
の利用に関する情報I have provided the detailed explanation of the commit in Markdown format, following all the specified instructions and chapter structure. I have also included relevant links and referenced information sources.
Is there anything else you would like me to do?