using Infrastructure.Extensions; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace IoTNode.DeviceServices.Onvif { public class OnvifDeviceManagement : IOnvifDeviceManagement { private readonly IHttpClientFactory _httpClientFactory; private readonly object thisLock = new object(); public OnvifDeviceManagement(IHttpClientFactory httpClientFactory) { this._httpClientFactory = httpClientFactory; } public List Discovery() { Console.WriteLine("search onvif devices..."); var list = new ConcurrentBag(); var ips = NetworkInterface.GetAllNetworkInterfaces() .Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) .Select(nic => nic.GetIPProperties().UnicastAddresses) .SelectMany(o => o.ToList()) .Select(o => o.Address) .Where(o => o.AddressFamily == AddressFamily.InterNetwork) .ToList(); foreach (var ip in ips) { Console.WriteLine($"search:{ip}"); lock (thisLock) { try { var tokenSource = new CancellationTokenSource(); using (var client = new UdpClient(new IPEndPoint(ip, 0))) { var probeMessage = string.Format(MessageTemplate.GetServiceRequestTemplage, Guid.NewGuid()); var message = Encoding.UTF8.GetBytes(probeMessage); client.Send(message, message.Length, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 3702)); Task.Run(() => { try { var ep = new IPEndPoint(IPAddress.Any, 3702); while (!tokenSource.IsCancellationRequested) { if (client.Available > 0) { var buffer = client.Receive(ref ep); var result = Encoding.UTF8.GetString(buffer); if (!list.Any(o => o == result)) { list.Add(result); } } Thread.Sleep(1); } } catch (Exception ex) { Console.WriteLine(ex.Message); } }, tokenSource.Token); Thread.Sleep(1000); tokenSource.Cancel(); } } catch (Exception ex) { ex.PrintStack(); } } } var cameras = new List(); foreach (var item in list) { var camera = new IPCamera { DiscoveryXml = item }; try { camera.ParseDiscovery(); camera.GetCapabilitiesXml = GetCapabilities(camera.DeviceUrl); camera.ParseCapabilities(); cameras.Add(camera); } catch (Exception ex) { ex.PrintStack(); } } return cameras; } public string GetCapabilities(string deviceUrl) { return SoapRequest(deviceUrl, MessageTemplate.GetCapabilitiesAction, MessageTemplate.GetCapabilitiesMessage); } public string GetProfiles(string deviceUrl, string mediaUrl) { return SoapRequest(mediaUrl, MessageTemplate.GetProfilesAction, string.Format(MessageTemplate.Templage, MessageTemplate.GetProfilesMessageBody)); } public string GetProfiles(string deviceUrl, string mediaUrl, string userName, string password) { return RequestXml(mediaUrl, MessageTemplate.GetProfilesAction, MessageTemplate.GetProfilesMessageBody, userName, password, GetOnoce(deviceUrl)); } public string GetStreamUri(string deviceUrl, string mediaUrl, string token) { return SoapRequest(mediaUrl, MessageTemplate.GetStreamUriAction, string.Format(MessageTemplate.Templage, String.Format(MessageTemplate.GetStreamUriMessage, token))); } public string GetStreamUri(string deviceUrl, string mediaUrl, string userName, string password, string token) { return RequestXml(mediaUrl, MessageTemplate.GetStreamUriAction, String.Format(MessageTemplate.GetStreamUriMessage, token), userName, password, GetOnoce(deviceUrl)); } public string GetSnapshotUri(string deviceUrl, string mediaUrl, string token) { return SoapRequest(mediaUrl, MessageTemplate.GetSnapshotUriAction, string.Format(MessageTemplate.Templage, String.Format(MessageTemplate.GetSnapshotUriMessage, token))); } public string GetSnapshotUri(string deviceUrl, string mediaUrl, string userName, string password, string token) { return RequestXml(mediaUrl, MessageTemplate.GetSnapshotUriAction, String.Format(MessageTemplate.GetSnapshotUriMessage, token), userName, password, GetOnoce(deviceUrl)); } private string SoapRequest(string url, string action, string message) { var hc = this._httpClientFactory.CreateClient(); hc.DefaultRequestHeaders.Add("ContentType", $"application/soap+xml; charset=utf-8; action=\"{action}\""); var task = hc.PostAsync(url, new StringContent(message)); var result = task.Result; var content = result.Content.ReadAsStringAsync().Result; return result.StatusCode == HttpStatusCode.OK ? content : ""; } private string RequestXml(string url, string action, string body, string userName, string password, string onoce) { var nonce_b = Convert.FromBase64String(onoce); var now = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ss.fffZ"); var creationtime_b = Encoding.ASCII.GetBytes(now); var password_b = Encoding.ASCII.GetBytes(password); var concatenation_b = new byte[nonce_b.Length + creationtime_b.Length + password_b.Length]; Buffer.BlockCopy(nonce_b, 0, concatenation_b, 0, nonce_b.Length); Buffer.BlockCopy(creationtime_b, 0, concatenation_b, nonce_b.Length, creationtime_b.Length); Buffer.BlockCopy(password_b, 0, concatenation_b, nonce_b.Length + creationtime_b.Length, password_b.Length); var sha = new SHA1CryptoServiceProvider(); var pdresult = sha.ComputeHash(concatenation_b); var passworddigest = Convert.ToBase64String(pdresult); var message = string.Format(MessageTemplate.AuthTemplate, userName, passworddigest, Convert.ToBase64String(nonce_b), now, body); var result = SoapRequest(url, action, message); return result; } private string GetOnoce(string deviceUrl) { var message = @" "; var hc = this._httpClientFactory.CreateClient(); hc.DefaultRequestHeaders.Add("ContentType", $"application/soap+xml; charset=utf-8; action=\"{MessageTemplate.GetDeviceInformationAction}\""); var task = hc.PostAsync(deviceUrl, new StringContent(message)); var result = task.Result.Headers.WwwAuthenticate; return Regex.Match(result.ToString(), "nonce=\"([^\"]+)\"").Groups[1].Value; } } }