package onvif import ( "github.com/AlexxIT/go2rtc/pkg/core" "net" "net/url" "regexp" "strconv" "strings" "time" ) func FindTagValue(b []byte, tag string) string { re := regexp.MustCompile(tag + `[^>]*>([^<]+)`) m := re.FindSubmatch(b) if len(m) != 2 { return "" } return string(m[1]) } // UUID - generate something like 44302cbf-0d18-4feb-79b3-33b575263da3 func UUID() string { s := core.RandString(32, 16) return s[:8] + "-" + s[8:12] + "-" + s[12:16] + "-" + s[16:20] + "-" + s[20:] } func DiscoveryStreamingHosts() ([]string, error) { conn, err := net.ListenPacket("udp4", ":0") if err != nil { return nil, err } // https://www.onvif.org/wp-content/uploads/2016/12/ONVIF_Feature_Discovery_Specification_16.07.pdf // 5.3 Discovery Procedure: msg := ` http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe urn:uuid:` + UUID() + ` urn:schemas-xmlsoap-org:ws:2005:04:discovery ` addr := &net.UDPAddr{ IP: net.IP{239, 255, 255, 250}, Port: 3702, } if _, err = conn.WriteTo([]byte(msg), addr); err != nil { return nil, err } if err = conn.SetReadDeadline(time.Now().Add(time.Second * 3)); err != nil { return nil, err } var hosts []string b := make([]byte, 8192) for { n, _, err := conn.ReadFrom(b) if err != nil { break } // ignore printers, etc if !strings.Contains(string(b[:n]), "onvif") { continue } //log.Printf("[onvif] discovery response:\n%s", b[:n]) rawURL := FindTagValue(b[:n], "XAddrs") if rawURL == "" { continue } u, err := url.Parse(rawURL) if err != nil { continue } if u.Scheme != "http" { continue } hosts = append(hosts, u.Host) } return hosts, nil } func atoi(s string) int { if s == "" { return 0 } i, err := strconv.Atoi(s) if err != nil { return -1 } return i }