macOSで *.localhost を 127.0.0.1 に解決する
はじめに
手元の計算機で ping -c1 foobar.localhost
を試してみてほしい。どこにpingが送られるだろうか?Linuxでは localhost
と同様ループバックアドレス 127.0.0.1
に解決されることが多いようだ。このふるまいについて "Let 'localhost' be localhost." という標準化への草稿がある
一方、現時点ではmacOSだと Unknown host
とエラーになる
$ ping -c1 foobar.localhost
ping: cannot resolve foobar.localhost: Unknown host
*.localhost
がループバックアドレスに解決されるとどのような嬉しみがあるか。例えばGitHub Pagesのような、サブドメインがパラメタとして機能するWebアプリケーションの開発に便利だろう
ここではmacOSでも *.localhost
がループバックアドレスに解決されるようにする一方法を解説する。この方法はまず何でもループバックアドレスに解決するDNSリゾルバをローカルで起動し、それを /etc/resolver
に登録するという2つのステップからなる
単純なDNSリゾルバ
DNSリゾルバを書くのに、ここではGoと github.com/miekg/dns
を使う。このDNSリゾルバがすることは10053番ポートで待ち受けて、クエリの種類が A
のときにループバックアドレスを返すことだけ
package main
import (
"net"
"github.com/miekg/dns"
)
func main() {
if err := dns.ListenAndServe(":10053", "udp4", dns.HandlerFunc(handle)); err != nil {
panic(err)
}
}
func handle(w dns.ResponseWriter, r *dns.Msg) {
if r.Opcode != dns.OpcodeQuery {
return
}
m := dns.Msg{Compress: true}
m.SetReply(r)
for _, q := range m.Question {
if q.Qtype != dns.TypeA {
continue
}
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET},
A: net.IPv4(127, 0, 0, 1),
})
}
if err := w.WriteMsg(&m); err != nil {
panic(err)
}
}
これを実行すると10053番ポートでDNSリゾルバが起動する。停止する際には Ctrl-C
を入力する
$ go run main.go
# Ctrl-C でDNSリゾルバを停止する
$ dig @127.0.0.1 -p10053 foobar.localhost A
; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p10053 foobar.localhost A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56078
;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;foobar.localhost. IN A
;; ANSWER SECTION:
foobar.localhost. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#10053(127.0.0.1)
;; WHEN: Fri Jul 02 10:12:58 JST 2021
;; MSG SIZE rcvd: 50
DNSリゾルバの登録
DNSリゾルバを起動したら、以下のようなテキストファイルを /etc/resolver/localhost
として保存することで登録される
nameserver 127.0.0.1
port 10053
$ cat <<EOF | sudo tee /etc/resolver/localhost
> nameserver 127.0.0.1
> port 10053
> EOF
Password: # パスワードを入力する
nameserver 127.0.0.1
port 10053
登録が完了すると、任意の *.localhost
が 127.0.0.1
に解決されるようになる
$ ping -c1 foobar.localhost
PING foobar.localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.035 ms
--- foobar.localhost ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.035/0.035/0.035/0.000 ms
Ctrl-C
でDNSリゾルバを停止したら、登録も削除しておくべき
$ sudo rm /etc/resolver/localhost
Password: # パスワードを入力する
おわりに
macOSでも *.localhost
が 127.0.0.1
に解決されるようにする方法を解説した
もともと自分で使うために github.com/ichiban/llbl
というユーティリティを書いていたが、冒頭のtweetで思い出し、他のmacOSユーザにも役立つかもと思い書いた
Discussion