Files
yovision/Sense/server/app/sense/adapters/onvif/discovery.go
T

81 lines
2.5 KiB
Go

package onvif
import (
"context"
"fmt"
"net"
"strings"
"time"
)
const discoveryProbe = `<?xml version="1.0"?><e:Envelope xmlns:e="http://www.w3.org/2003/05/soap-envelope" xmlns:w="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:dn="http://www.onvif.org/ver10/network/wsdl"><e:Header><w:MessageID>uuid:sense-controlled-discovery</w:MessageID><w:To e:mustUnderstand="true">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To><w:Action e:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</w:Action></e:Header><e:Body><d:Probe><d:Types>dn:NetworkVideoTransmitter</d:Types></d:Probe></e:Body></e:Envelope>`
func Discover(ctx context.Context, localIP string, timeout time.Duration) ([]string, error) {
ip := net.ParseIP(localIP)
if ip == nil {
return nil, fmt.Errorf("approved discovery interface is not configured")
}
approved := false
interfaces, _ := net.Interfaces()
for _, iface := range interfaces {
addresses, _ := iface.Addrs()
for _, address := range addresses {
if strings.HasPrefix(address.String(), ip.String()+"/") {
approved = true
}
}
}
if !approved {
return nil, fmt.Errorf("approved discovery IP is not a local interface")
}
connection, err := net.ListenUDP("udp4", &net.UDPAddr{IP: ip, Port: 0})
if err != nil {
return nil, err
}
defer connection.Close()
deadline := time.Now().Add(timeout)
_ = connection.SetDeadline(deadline)
target := &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 3702}
if _, err := connection.WriteToUDP([]byte(discoveryProbe), target); err != nil {
return nil, err
}
found := map[string]bool{}
buffer := make([]byte, 65535)
for {
select {
case <-ctx.Done():
return keys(found), nil
default:
}
n, _, err := connection.ReadFromUDP(buffer)
if err != nil {
if networkErr, ok := err.(net.Error); ok && networkErr.Timeout() {
return keys(found), nil
}
return nil, err
}
for _, value := range extractXAddrs(string(buffer[:n])) {
found[value] = true
}
}
}
func extractXAddrs(value string) []string {
startTag, endTag := "<d:XAddrs>", "</d:XAddrs>"
start := strings.Index(value, startTag)
if start < 0 {
return nil
}
end := strings.Index(value[start:], endTag)
if end < 0 {
return nil
}
return strings.Fields(value[start+len(startTag) : start+end])
}
func keys(values map[string]bool) []string {
result := make([]string, 0, len(values))
for value := range values {
result = append(result, value)
}
return result
}