Skip to content

Commit d802f8e

Browse files
committed
Make mdns responder case-insensitive
From [the RFC](https://github.com/openwrt/mdnsd/blob/master/rfc6762.txt#L2532-L2550): > The simple rules for case-insensitivity in Unicast DNS [RFC1034] [RFC1035] also apply in Multicast DNS; that is to say, in name comparisons, the lowercase letters "a" to "z" (0x61 to 0x7A) match their uppercase equivalents "A" to "Z" (0x41 to 0x5A). Hence, if a querier issues a query for an address record with the name "myprinter.local.", then a responder having an address record with the name "MyPrinter.local." should issue a response. No other automatic equivalences should be assumed.
1 parent 61c2740 commit d802f8e

File tree

2 files changed

+129
-2
lines changed

2 files changed

+129
-2
lines changed

conn.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"net"
1111
"net/netip"
12+
"strings"
1213
"sync"
1314
"time"
1415

@@ -980,7 +981,7 @@ func (c *Conn) readLoop(name string, pktConn ipPacketConn, inboundBufferSize int
980981
queryWantsV4 := question.Type == dnsmessage.TypeA
981982

982983
for _, localName := range c.localNames {
983-
if localName == question.Name.String() { //nolint:nestif
984+
if strings.EqualFold(localName, question.Name.String()) { //nolint:nestif
984985
var localAddress *netip.Addr
985986
if config.LocalAddress != nil {
986987
// this means the LocalAddress does not support link-local since
@@ -1150,7 +1151,7 @@ func (c *Conn) readLoop(name string, pktConn ipPacketConn, inboundBufferSize int
11501151
var answered []*query
11511152
for _, query := range queries {
11521153
queryCopy := query
1153-
if queryCopy.nameWithSuffix == answer.Name.String() {
1154+
if strings.EqualFold(queryCopy.nameWithSuffix, answer.Name.String()) {
11541155
addr, err := addrFromAnswerHeader(answer, parser)
11551156
if err != nil {
11561157
c.log.Warnf("[%s] failed to parse mDNS answer %v", c.name, err)

conn_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"net"
1414
"net/netip"
1515
"runtime"
16+
"strings"
1617
"testing"
1718
"time"
1819

@@ -67,6 +68,72 @@ func createListener6(t *testing.T) *net.UDPConn {
6768
return sock
6869
}
6970

71+
func sendAnswer(t *testing.T, sock *net.UDPConn, dst net.Addr, queryID uint16, name string, ip string) {
72+
t.Helper()
73+
74+
answer, err := createAnswer(queryID, name, netip.MustParseAddr(ip))
75+
check(t, err)
76+
77+
answerBytes, err := answer.Pack()
78+
check(t, err)
79+
80+
_, err = sock.WriteTo(answerBytes, dst)
81+
check(t, err)
82+
}
83+
84+
// createSimpleDNSResponder creates a case-insensitive DNS responder that always
85+
// responds with responseName. This function is required because the actual
86+
// responder will respond with the exact queried name, while we want to test
87+
// responding with a different variation of uppercase/lowercase letters.
88+
//
89+
// The caller of this function must call sock.Close().
90+
func createSimpleDNSResponder(t *testing.T, recordType dnsmessage.Type, name string, ip string) *net.UDPConn {
91+
t.Helper()
92+
sock := createListener4(t)
93+
94+
if !strings.HasSuffix(name, ".") {
95+
name += "."
96+
}
97+
98+
go func() {
99+
defer sock.Close() //nolint:errcheck
100+
101+
buf := make([]byte, 65536)
102+
103+
for {
104+
n, addr, err := sock.ReadFromUDP(buf)
105+
if errors.Is(err, net.ErrClosed) {
106+
return
107+
}
108+
check(t, err)
109+
110+
parser := dnsmessage.Parser{}
111+
header, err := parser.Start(buf[:n])
112+
check(t, err)
113+
114+
for {
115+
question, err := parser.Question()
116+
if errors.Is(err, dnsmessage.ErrSectionDone) {
117+
break
118+
}
119+
check(t, err)
120+
121+
if question.Type != recordType {
122+
continue
123+
}
124+
125+
if !strings.EqualFold(question.Name.String(), name) {
126+
continue
127+
}
128+
129+
sendAnswer(t, sock, addr, header.ID, name, ip)
130+
}
131+
}
132+
}()
133+
134+
return sock
135+
}
136+
70137
func TestValidCommunication(t *testing.T) {
71138
lim := test.TimeOut(time.Second * 10)
72139
defer lim.Stop()
@@ -597,6 +664,65 @@ func TestValidCommunicationIPv64Mixed(t *testing.T) {
597664
}
598665
}
599666

667+
func TestLocalNameCaseInsensitivity(t *testing.T) {
668+
lim := test.TimeOut(time.Second * 10)
669+
defer lim.Stop()
670+
671+
report := test.CheckRoutines(t)
672+
defer report()
673+
674+
loopbackIP := net.ParseIP("127.0.0.1")
675+
676+
aSock := createListener4(t)
677+
aServer, err := Server(ipv4.NewPacketConn(aSock), nil, &Config{
678+
LocalNames: []string{"pion-mdns-1.local"},
679+
LocalAddress: loopbackIP,
680+
IncludeLoopback: true,
681+
})
682+
check(t, err)
683+
684+
tests := []string{"pion-mdns-1.local", "PION-MDNS-1.local", "pion-MDNS-1.local"}
685+
for _, test := range tests {
686+
t.Run(test, func(t *testing.T) {
687+
_, addr, err := aServer.QueryAddr(context.Background(), test)
688+
check(t, err)
689+
if addr.String() != loopbackIP.String() {
690+
t.Fatalf("address mismatch: expected %s, but got %v\n", localAddress, addr)
691+
}
692+
})
693+
}
694+
695+
check(t, aServer.Close())
696+
}
697+
698+
func TestCommunicationCaseInsensitivity(t *testing.T) {
699+
lim := test.TimeOut(time.Second * 10)
700+
defer lim.Stop()
701+
702+
report := test.CheckRoutines(t)
703+
defer report()
704+
705+
aSock := createSimpleDNSResponder(t, dnsmessage.TypeA, "pion-MDNS-1.local", localAddress)
706+
707+
bSock := createListener4(t)
708+
bServer, err := Server(ipv4.NewPacketConn(bSock), nil, &Config{})
709+
check(t, err)
710+
711+
tests := []string{"pion-mdns-1.local", "pion-MDNS-1.local"}
712+
for _, test := range tests {
713+
t.Run(test, func(t *testing.T) {
714+
_, addr, err := bServer.QueryAddr(context.Background(), test)
715+
check(t, err)
716+
if addr.String() != localAddress {
717+
t.Fatalf("unexpected local address: %v", addr)
718+
}
719+
})
720+
}
721+
722+
check(t, aSock.Close())
723+
check(t, bServer.Close())
724+
}
725+
600726
func TestMultipleClose(t *testing.T) {
601727
lim := test.TimeOut(time.Second * 10)
602728
defer lim.Stop()

0 commit comments

Comments
 (0)