mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-27 04:36:12 +08:00
Update mDNS server handler
This commit is contained in:
@@ -13,7 +13,10 @@ import (
|
|||||||
"github.com/miekg/dns" // awesome library for parsing mDNS records
|
"github.com/miekg/dns" // awesome library for parsing mDNS records
|
||||||
)
|
)
|
||||||
|
|
||||||
const ServiceHAP = "_hap._tcp.local." // HomeKit Accessory Protocol
|
const (
|
||||||
|
ServiceDNSSD = "_services._dns-sd._udp.local."
|
||||||
|
ServiceHAP = "_hap._tcp.local." // HomeKit Accessory Protocol
|
||||||
|
)
|
||||||
|
|
||||||
type ServiceEntry struct {
|
type ServiceEntry struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
@@ -153,6 +156,7 @@ type Browser struct {
|
|||||||
Service string
|
Service string
|
||||||
|
|
||||||
Addr net.Addr
|
Addr net.Addr
|
||||||
|
Nets []*net.IPNet
|
||||||
Recv net.PacketConn
|
Recv net.PacketConn
|
||||||
Sends []net.PacketConn
|
Sends []net.PacketConn
|
||||||
|
|
||||||
@@ -165,7 +169,7 @@ type Browser struct {
|
|||||||
// Receiver will get multicast responses on senders requests.
|
// Receiver will get multicast responses on senders requests.
|
||||||
func (b *Browser) ListenMulticastUDP() error {
|
func (b *Browser) ListenMulticastUDP() error {
|
||||||
// 1. Collect IPv4 interfaces
|
// 1. Collect IPv4 interfaces
|
||||||
ip4s, err := InterfacesIP4()
|
nets, err := IPNets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -182,11 +186,12 @@ func (b *Browser) ListenMulticastUDP() error {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
for _, ip4 := range ip4s {
|
for _, ipn := range nets {
|
||||||
conn, err := lc1.ListenPacket(ctx, "udp4", ip4.String()+":5353") // same port important
|
conn, err := lc1.ListenPacket(ctx, "udp4", ipn.IP.String()+":5353") // same port important
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
b.Nets = append(b.Nets, ipn)
|
||||||
b.Sends = append(b.Sends, conn)
|
b.Sends = append(b.Sends, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,35 +370,39 @@ func NewServiceEntries(msg *dns.Msg, ip net.IP) (entries []*ServiceEntry) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func InterfacesIP4() ([]net.IP, error) {
|
// Common docker addresses (class B):
|
||||||
|
// https://en.wikipedia.org/wiki/Private_network
|
||||||
|
// - docker0 172.17.0.1/16
|
||||||
|
// - br-xxxx 172.18.0.1/16
|
||||||
|
// - hassio 172.30.32.1/23
|
||||||
|
var docker = net.IPNet{
|
||||||
|
IP: []byte{172, 16, 0, 0},
|
||||||
|
Mask: []byte{255, 240, 0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPNets() ([]*net.IPNet, error) {
|
||||||
intfs, err := net.Interfaces()
|
intfs, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var ips []net.IP
|
var nets []*net.IPNet
|
||||||
|
|
||||||
loop:
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
if intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagLoopback != 0 {
|
if intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagLoopback != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err := intf.Addrs()
|
addrs, _ := intf.Addrs()
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
switch v := addr.(type) {
|
switch v := addr.(type) {
|
||||||
case *net.IPNet:
|
case *net.IPNet:
|
||||||
if ip := v.IP.To4(); ip != nil {
|
if ip := v.IP.To4(); ip != nil && !docker.Contains(ip) {
|
||||||
ips = append(ips, ip)
|
nets = append(nets, v)
|
||||||
continue loop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return nets, nil
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,11 @@ func Serve(service string, entries []*ServiceEntry) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) Serve(entries []*ServiceEntry) error {
|
func (b *Browser) Serve(entries []*ServiceEntry) error {
|
||||||
var msg dns.Msg
|
names := make(map[string]*ServiceEntry, len(entries))
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.name() + "." + b.Service
|
||||||
|
names[name] = entry
|
||||||
|
}
|
||||||
|
|
||||||
buf := make([]byte, 1500)
|
buf := make([]byte, 1500)
|
||||||
for {
|
for {
|
||||||
@@ -29,129 +33,130 @@ func (b *Browser) Serve(entries []*ServiceEntry) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = msg.Unpack(buf[:n]); err != nil {
|
var req dns.Msg // request
|
||||||
|
if err = req.Unpack(buf[:n]); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !HasQuestionPTP(&msg, b.Service) {
|
// skip messages without Questions
|
||||||
|
if req.Question == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteIP := addr.(*net.UDPAddr).IP
|
remoteIP := addr.(*net.UDPAddr).IP
|
||||||
localIP := MatchLocalIP(remoteIP)
|
localIP := b.MatchLocalIP(remoteIP)
|
||||||
|
|
||||||
|
// skip messages from unknown networks (can be docker network)
|
||||||
if localIP == nil {
|
if localIP == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
answer, err := NewDNSAnswer(entries, b.Service, localIP).Pack()
|
var res dns.Msg // response
|
||||||
|
for _, q := range req.Question {
|
||||||
|
if q.Qtype != dns.TypePTR || q.Qclass != dns.ClassINET {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Name == ServiceDNSSD {
|
||||||
|
AppendDNSSD(&res, b.Service)
|
||||||
|
} else if q.Name == b.Service {
|
||||||
|
for _, entry := range entries {
|
||||||
|
AppendEntry(&res, entry, b.Service, localIP)
|
||||||
|
}
|
||||||
|
} else if entry, ok := names[q.Name]; ok {
|
||||||
|
AppendEntry(&res, entry, b.Service, localIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Answer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res.MsgHdr.Response = true
|
||||||
|
res.MsgHdr.Authoritative = true
|
||||||
|
|
||||||
|
data, err := res.Pack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, send := range b.Sends {
|
for _, send := range b.Sends {
|
||||||
_, _ = send.WriteTo(answer, MulticastAddr)
|
_, _ = send.WriteTo(data, MulticastAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasQuestionPTP(msg *dns.Msg, name string) bool {
|
func (b *Browser) MatchLocalIP(remote net.IP) net.IP {
|
||||||
for _, q := range msg.Question {
|
for _, ipn := range b.Nets {
|
||||||
if q.Qtype == dns.TypePTR && q.Name == name {
|
if ipn.Contains(remote) {
|
||||||
return true
|
return ipn.IP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDNSAnswer(entries []*ServiceEntry, service string, ip net.IP) *dns.Msg {
|
func AppendDNSSD(msg *dns.Msg, service string) {
|
||||||
msg := dns.Msg{
|
msg.Answer = append(
|
||||||
MsgHdr: dns.MsgHdr{
|
msg.Answer,
|
||||||
Response: true,
|
&dns.PTR{
|
||||||
Authoritative: true,
|
Hdr: dns.RR_Header{
|
||||||
|
Name: ServiceDNSSD, // _services._dns-sd._udp.local.
|
||||||
|
Rrtype: dns.TypePTR, // 12
|
||||||
|
Class: dns.ClassINET, // 1
|
||||||
|
Ttl: 4500,
|
||||||
|
},
|
||||||
|
Ptr: service, // _home-assistant._tcp.local.
|
||||||
},
|
},
|
||||||
}
|
)
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
ptrName := entry.name() + "." + service
|
|
||||||
srvName := entry.name() + ".local."
|
|
||||||
|
|
||||||
msg.Answer = append(
|
|
||||||
msg.Answer,
|
|
||||||
&dns.PTR{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: service,
|
|
||||||
Rrtype: dns.TypePTR,
|
|
||||||
Class: dns.ClassINET,
|
|
||||||
Ttl: 4500,
|
|
||||||
},
|
|
||||||
Ptr: ptrName,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
msg.Extra = append(
|
|
||||||
msg.Extra,
|
|
||||||
&dns.TXT{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: ptrName,
|
|
||||||
Rrtype: dns.TypeTXT,
|
|
||||||
Class: ClassCacheFlush,
|
|
||||||
Ttl: 4500,
|
|
||||||
},
|
|
||||||
Txt: entry.TXT(),
|
|
||||||
},
|
|
||||||
&dns.SRV{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: ptrName,
|
|
||||||
Rrtype: dns.TypeSRV,
|
|
||||||
Class: ClassCacheFlush,
|
|
||||||
Ttl: 120,
|
|
||||||
Rdlength: 0,
|
|
||||||
},
|
|
||||||
Port: entry.Port,
|
|
||||||
Target: srvName,
|
|
||||||
},
|
|
||||||
&dns.A{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: srvName,
|
|
||||||
Rrtype: dns.TypeA,
|
|
||||||
Class: ClassCacheFlush,
|
|
||||||
Ttl: 120,
|
|
||||||
Rdlength: 0,
|
|
||||||
},
|
|
||||||
A: ip,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MatchLocalIP(remote net.IP) net.IP {
|
func AppendEntry(msg *dns.Msg, entry *ServiceEntry, service string, ip net.IP) {
|
||||||
intfs, err := net.Interfaces()
|
ptrName := entry.name() + "." + service
|
||||||
if err != nil {
|
srvName := entry.name() + ".local."
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, intf := range intfs {
|
msg.Answer = append(
|
||||||
if intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagLoopback != 0 {
|
msg.Answer,
|
||||||
continue
|
&dns.PTR{
|
||||||
}
|
Hdr: dns.RR_Header{
|
||||||
|
Name: service, // _home-assistant._tcp.local.
|
||||||
addrs, err := intf.Addrs()
|
Rrtype: dns.TypePTR, // 12
|
||||||
if err != nil {
|
Class: dns.ClassINET, // 1
|
||||||
continue
|
Ttl: 4500,
|
||||||
}
|
},
|
||||||
|
Ptr: ptrName, // Home\ Assistant._home-assistant._tcp.local.
|
||||||
for _, addr := range addrs {
|
},
|
||||||
switch v := addr.(type) {
|
)
|
||||||
case *net.IPNet:
|
msg.Extra = append(
|
||||||
if local := v.IP.To4(); local != nil && v.Contains(remote) {
|
msg.Extra,
|
||||||
return local
|
&dns.TXT{
|
||||||
}
|
Hdr: dns.RR_Header{
|
||||||
}
|
Name: ptrName, // Home\ Assistant._home-assistant._tcp.local.
|
||||||
}
|
Rrtype: dns.TypeTXT, // 16
|
||||||
}
|
Class: ClassCacheFlush, // 32769
|
||||||
|
Ttl: 4500,
|
||||||
return nil
|
},
|
||||||
|
Txt: entry.TXT(),
|
||||||
|
},
|
||||||
|
&dns.SRV{
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: ptrName, // Home\ Assistant._home-assistant._tcp.local.
|
||||||
|
Rrtype: dns.TypeSRV, // 33
|
||||||
|
Class: ClassCacheFlush, // 32769
|
||||||
|
Ttl: 120,
|
||||||
|
},
|
||||||
|
Port: entry.Port, // 8123
|
||||||
|
Target: srvName, // 963f1fa82b7142809711cebe7c826322.local.
|
||||||
|
},
|
||||||
|
&dns.A{
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: srvName, // 963f1fa82b7142809711cebe7c826322.local.
|
||||||
|
Rrtype: dns.TypeA, // 1
|
||||||
|
Class: ClassCacheFlush, // 32769
|
||||||
|
Ttl: 120,
|
||||||
|
},
|
||||||
|
A: ip,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user