package onvif import ( "context" "encoding/xml" "errors" "io" "net" "sort" "strings" "time" ) var ( ErrDiscoveryNotConfigured = errors.New("未配置获准的发现网卡") ErrDiscoveryInterface = errors.New("配置的发现地址不是本机网卡") ) const discoveryProbe = `uuid:sense-controlled-discoveryurn:schemas-xmlsoap-org:ws:2005:04:discoveryhttp://schemas.xmlsoap.org/ws/2005/04/discovery/Probedn:NetworkVideoTransmitter` func Discover(ctx context.Context, localIP string, timeout time.Duration, policy Policy) ([]string, error) { ip := net.ParseIP(strings.TrimSpace(localIP)) if ip == nil { return nil, ErrDiscoveryNotConfigured } approved := false interfaces, _ := net.Interfaces() for _, iface := range interfaces { addresses, _ := iface.Addrs() for _, address := range addresses { host, _, _ := net.ParseCIDR(address.String()) if host != nil && host.Equal(ip) { approved = true } } } if !approved { return nil, ErrDiscoveryInterface } connection, err := net.ListenUDP("udp4", &net.UDPAddr{IP: ip}) if err != nil { return nil, err } defer connection.Close() if timeout <= 0 { timeout = 3 * time.Second } _ = connection.SetDeadline(time.Now().Add(timeout)) if _, err = connection.WriteToUDP([]byte(discoveryProbe), &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 3702}); err != nil { return nil, err } found := map[string]bool{} buffer := make([]byte, 65535) for { n, _, readErr := connection.ReadFromUDP(buffer) if readErr != nil { if e, ok := readErr.(net.Error); ok && e.Timeout() { break } return nil, readErr } for _, candidate := range extractXAddrs(string(buffer[:n])) { if _, _, validErr := policy.ValidateURL(ctx, candidate, "http", "https"); validErr == nil { found[candidate] = true } } } result := make([]string, 0, len(found)) for value := range found { result = append(result, value) } sort.Strings(result) return result, nil } func extractXAddrs(value string) []string { decoder := xml.NewDecoder(strings.NewReader(value)) for { token, err := decoder.Token() if errors.Is(err, io.EOF) { break } if err != nil { return nil } start, ok := token.(xml.StartElement) if !ok || start.Name.Local != "XAddrs" { continue } var addresses string if decoder.DecodeElement(&addresses, &start) == nil { return strings.Fields(addresses) } } return nil }