I am working with an IP camera that supports Audio output(Audio Back-channel). What I want to do is The live stream my PC microphone audio data over provided RTSP URL so that what ever said at PC microphone will be audible at camera speaker end. I have read about onvif streaming specification that tells me that once I got RTSP url of camera media I have to send my audio data over provided rtsp url for audio output at camera end. Also my camera supports Onvif profile T.
What I have tried so far is -
public static RtspClient rtspClient;
public static IWaveIn sourceStream;
private static void CallAudio()
{
string CameraIp = "192.168.1.69";
string UserName = "admin";
string Password = "admin123";
var ClientMessageInspector = new ClientMessageInspector(UserName, Password);
//Call Device Url and get Services.
string DeviceServiceUrl = "http://" + CameraIp + "/onvif/device_service";
var deviceClient = new DeviceClient("DeviceBinding", new EndpointAddress(DeviceServiceUrl));
deviceClient.Endpoint.Behaviors.Add(ClientMessageInspector);
var getServices = deviceClient.GetServices(false);
//Call media2 getStreamingUri.
string url = "http://" + CameraIp + "/onvif/media2_service";
var Media2Client = new Media2Client("Media2Binding", new EndpointAddress(url));
Media2Client.Endpoint.Behaviors.Add(ClientMessageInspector);
var media2GetProfiles = Media2Client.GetProfiles(null, null);
var resp = Media2Client.GetAudioDecoderConfigurationOptions(null, null);
var responseGetAudioStreamUri = Media2Client.GetStreamUri("tcp", profiles[0].token); //This gets rtsp url of media from camera.
rtspClient = new RtspClient(responseGetAudioStreamUri, UserName, Password);
sourceStream = new WaveInEvent();
sourceStream.WaveFormat = new WaveFormat(64, 8, 1); //8000 16
sourceStream.DataAvailable += new EventHandler<WaveInEventArgs>(SourceStream_DataAvailable);
sourceStream.StartRecording();
Console.ReadKey();
}
//This method gets data from PC microphone and enocodes it into Mu-Law G711 and send to rtsp url.
private static void SourceStream_DataAvailable(object sender, WaveInEventArgs e)
{
byte[] encoded = TwoWayAudio_Encode_MuLaw(e.Buffer, 0, e.BytesRecorded);
rtspClient.SendData(encoded, encoded.Length, 3);
}
private static byte[] TwoWayAudio_Encode_MuLaw(byte[] data, int offset, int length)
{
byte[] encoded = new byte[length / 2];
int outIndex = 0;
for (int n = 0; n < length; n += 2)
{
encoded[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(data, offset + n));
}
return encoded;
}
The rtsp client that I am using in my project for Describe, Setup and Play of rtsp URL is taken from this github repo https://github.com/BogdanovKirill/RtspClientSharp.
Rtspclient.cs
using Rtsp;
using Rtsp.Messages;
using Rtsp.Sdp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace Rtsp
{
public class RtspClient
{
private RtspListener rtsp_client;
private RtspTcpTransport tcp_socket;
public string url;
public bool canPlay = false;
public string username;
public string password;
public ushort seqNo = 0;
public event EventHandler<string> RtspError;
public event EventHandler<byte[]> RtpDataReceived;
public Stopwatch stopwatch { get; private set; }
public RtspClient(string _url, string _username, string _password)
{
url = _url;
username = _username;
password = _password;
var uri = new Uri(_url);
tcp_socket = new RtspTcpTransport(uri.Host, 554); // 554);
if (tcp_socket.Connected == false)
{
Console.WriteLine("Error - did not connect");
return;
}
// Connect a RTSP Listener to the TCP Socket to send messages and listen for replies
rtsp_client = new RtspListener(tcp_socket);
rtsp_client.MessageReceived += Rtsp_client_MessageReceived;
rtsp_client.DataReceived += DataReceived;
rtsp_client.Start(); // start reading messages from the server
rtsp_client.AutoReconnect = true;
RtspRequest describe_message = new RtspRequestDescribe();
describe_message.RtspUri = uri;
describe_message.AddHeader("Accept: application/sdp");
describe_message.AddHeader("Require: www.onvif.org/ver20/backchannel");
rtsp_client.SendMessage(describe_message);
stopwatch = new Stopwatch();
stopwatch.Start();
}
private void DataReceived(object sender, RtspChunkEventArgs e)
{
int rtp_version = (e.Message.Data[0] >> 6);
int rtp_padding = (e.Message.Data[0] >> 5) & 0x01;
int rtp_extension = (e.Message.Data[0] >> 4) & 0x01;
int rtp_csrc_count = (e.Message.Data[0] >> 0) & 0x0F;
int rtp_marker = (e.Message.Data[1] >> 7) & 0x01;
int rtp_payload_type = (e.Message.Data[1] >> 0) & 0x7F;
uint rtp_sequence_number = ((uint)e.Message.Data[2] << 8) + (uint)(e.Message.Data[3]);
uint rtp_timestamp = ((uint)e.Message.Data[4] << 24) + (uint)(e.Message.Data[5] << 16) + (uint)(e.Message.Data[6] << 8) + (uint)(e.Message.Data[7]);
uint rtp_ssrc = ((uint)e.Message.Data[8] << 24) + (uint)(e.Message.Data[9] << 16) + (uint)(e.Message.Data[10] << 8) + (uint)(e.Message.Data[11]);
int rtp_payload_start = 4 // V,P,M,SEQ
+ 4 // time stamp
+ 4 // ssrc
+ (4 * rtp_csrc_count); // zero or more csrcs
uint rtp_extension_id = 0;
uint rtp_extension_size = 0;
if (rtp_extension == 1)
{
rtp_extension_id = ((uint)e.Message.Data[rtp_payload_start + 0] << 8) + (uint)(e.Message.Data[rtp_payload_start + 1] << 0);
rtp_extension_size = ((uint)e.Message.Data[rtp_payload_start + 2] << 8) + (uint)(e.Message.Data[rtp_payload_start + 3] << 0);
rtp_payload_start += 4 + (int)rtp_extension_size; // extension header and extension payload
}
Console.WriteLine("RTP Data"
+ " V=" + rtp_version
+ " P=" + rtp_padding
+ " X=" + rtp_extension
+ " CC=" + rtp_csrc_count
+ " M=" + rtp_marker
+ " PT=" + rtp_payload_type
+ " Seq=" + rtp_sequence_number
+ " Time=" + rtp_timestamp
+ " SSRC=" + rtp_ssrc
+ " Size=" + e.Message.Data.Length);
// If rtp_marker is '1' then this is the final transmission for this packet.
// If rtp_marker is '0' we need to accumulate data with the same timestamp
// ToDo - Check Timestamp matches
// Add to the tempoary_rtp List
if (rtp_payload_type == 98 || rtp_payload_type == 0)
{
byte[] rtp_payload = new byte[e.Message.Data.Length - rtp_payload_start]; // payload with RTP header removed
System.Array.Copy(e.Message.Data, rtp_payload_start, rtp_payload, 0, rtp_payload.Length); // copy payload
RtpDataReceived?.Invoke(null, rtp_payload);
}
}
public bool SendData(byte[] data, int count, int channel)
{
byte[] rtp_packet = new byte[12 + data.Length];
int rtp_version = 2;
int rtp_padding = 0;
int rtp_extension = 0;
int rtp_csrc_count = 0;
int rtp_marker = 1; // set to 1 if the last NAL in the array
//int rtp_payload_type = 98;
int rtp_payload_type = 0;
RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type);
RTPPacketUtil.WriteSequenceNumber(rtp_packet, seqNo);
seqNo++;
RTPPacketUtil.WriteTS(rtp_packet, (uint)stopwatch.ElapsedMilliseconds);
UInt32 empty_ssrc = 1293847657;
RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc);
// Now append the raw NAL
System.Array.Copy(data, 0, rtp_packet, 12, data.Length);
if (canPlay)
{
rtsp_client.SendData(channel, rtp_packet);
return true;
}
else return false;
}
private void Rtsp_client_MessageReceived(object sender, RtspChunkEventArgs e)
{
RtspResponse message = e.Message as RtspResponse;
if (message.ReturnCode == 500)
{
RtspError?.Invoke(this, "Internal Server Error");
}
if (message.ReturnCode == 401)
{
Rtsp.Messages.RtspRequest msg = null;
switch (message.OriginalRequest.Method)
{
case "DESCRIBE":
msg = new RtspRequestDescribe();
break;
case "SETUP":
msg = new RtspRequestSetup();
break;
default:
break;
}
msg.RtspUri = new Uri(url);
var header = message.Headers["WWW-Authenticate"];
var _realm = GrabHeaderVar("realm", header);
var _nonce = GrabHeaderVar("nonce", header);
var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", username, _realm, password));
var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", message.OriginalRequest.Method, url));
var digestResponse = CalculateMd5Hash(string.Format("{0}:{1}:{2}", ha1, _nonce, ha2));
var digest = string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\" ",
username, _realm, _nonce, url, digestResponse);
msg.AddHeader("Authorization: " + digest);
msg.AddHeader("Accept: application/sdp");
rtsp_client.SendMessage(msg);
return;
}
Console.WriteLine("Received " + message.OriginalRequest.ToString());
if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestDescribe)
{
// Got a reply for DESCRIBE
// Examine the SDP
Console.Write(Encoding.UTF8.GetString(message.Data));
SdpFile sdp_data;
using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
{
sdp_data = SdpFile.Read(sdp_stream);
}
// Process each 'Media' Attribute in the SDP.
// If the attribute is for Video, then send a SETUP
for (int x = 0; x < sdp_data.Medias.Count; x++)
{
if (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.audio || sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.video)
{
// seach the atributes for control, fmtp and rtpmap
String control = ""; // the "track" or "stream id"
String fmtp = ""; // holds SPS and PPS
String rtpmap = ""; // holds the Payload format, 96 is often used with H264
foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
{
if (attrib.Key.Equals("control")) control = attrib.Value;
if (attrib.Key.Equals("fmtp")) fmtp = attrib.Value;
if (attrib.Key.Equals("rtpmap")) rtpmap = attrib.Value;
}
// Get the Payload format number for the Video Stream
String[] split_rtpmap = rtpmap.Split(' ');
var video_payload = 0;
bool result = Int32.TryParse(split_rtpmap[0], out video_payload);
// Send SETUP for the Video Stream
// using Interleaved mode (RTP frames over the RTSP socket)
Rtsp.Messages.RtspRequest setup_message = new Rtsp.Messages.RtspRequestSetup();
setup_message.RtspUri = new Uri(url + "/" + control);
//setup_message.AddHeader("Transport: RTP/AVP/TCP;interleaved=0");
setup_message.AddHeader("Require: www.onvif.org/ver20/backchannel");
rtsp_client.SendMessage(setup_message);
}
}
}
if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestSetup)
{
// Got Reply to SETUP
Console.WriteLine("Got reply from Setup. Session is " + message.Session);
String session = message.Session; // Session value used with Play, Pause, Teardown
// Send PLAY
RtspRequest play_message = new RtspRequestPlay();
play_message.RtspUri = new Uri(url);
play_message.Session = session;
play_message.AddHeader("Require: www.onvif.org/ver20/backchannel");
rtsp_client.SendMessage(play_message);
}
if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestPlay)
{
// Got Reply to PLAY
Console.WriteLine("Got reply from Play " + message.Command);
canPlay = true;
}
}
private static string GrabHeaderVar(string varName, string header)
{
var regHeader = new Regex(string.Format(#"{0}=""([^""]*)""", varName));
var matchHeader = regHeader.Match(header);
if (matchHeader.Success)
return matchHeader.Groups[1].Value;
throw new ApplicationException(string.Format("Header {0} not found", varName));
}
private static string CalculateMd5Hash(string input)
{
var inputBytes = Encoding.ASCII.GetBytes(input);
var hash = MD5.Create().ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var b in hash)
sb.Append(b.ToString("x2"));
return sb.ToString();
}
public void Dispose()
{
rtsp_client.Stop();
rtsp_client.Dispose();
}
}
}
So before calling any rtsp method a i have added Rtsp Require: www.onvif.org/ver20/backchannel which is important for checking whether camera support AudioBack channel.
The output i get after calling Describe, Setup and play is Ok.
Received Rtsp.Messages.RtspRequestDescribe
v=0
o=- 0 0 IN IP4 192.168.1.69
s=LIVE VIEW
c=IN IP4 0.0.0.0
t=0 0
a=control:rtsp://192.168.1.69/rtsp_tunnel?p=0&h26x=4&aon=1&aud=0
m=video 0 RTP/AVP 35
a=rtpmap:35 H264/90000
a=control:rtsp://192.168.1.69/rtsp_tunnel?p=0&h26x=4&aon=1&aud=0&stream=video
a=recvonly
a=fmtp:35 packetization-mode=1;profile-level-id=4d0029;sprop-parameter-
sets=Z00AKZpkA8ARPy4C1BQEFAg=,aO48gA==
m=audio 0 RTP/AVP 96
a=rtpmap:96 mpeg4-generic/16000/1
a=fmtp:96 streamtype=5; profile-level-id=5; mode=AAC-hbr; config=1408; SizeLength=13; IndexLength=3;
IndexDeltaLength=3
a=control:rtsp://192.168.1.69/rtsp_tunnel?p=0&h26x=4&aon=1&aud=0&stream=audio
a=recvonly
m=audio 0 RTP/AVP 0
a=rtpmap:0 PCMU/8000/1
a=control:rtsp://192.168.1.69/rtsp_tunnel?p=0&h26x=4&aon=1&aud=0&stream=backchannel
a=sendonly
Received Rtsp.Messages.RtspRequestSetup
Got reply from Setup. Session is 12346e9856840dc
Received Rtsp.Messages.RtspRequestSetup
Got reply from Setup. Session is 12346e9856840dc
Received Rtsp.Messages.RtspRequestSetup
Got reply from Setup. Session is 12346e9856840dc
Received Rtsp.Messages.RtspRequestPlay
Got reply from Play RTSP/1.0 200 OK
Received Rtsp.Messages.RtspRequestPlay
Got reply from Play RTSP/1.0 200 OK
Received Rtsp.Messages.RtspRequestPlay
Got reply from Play RTSP/1.0 200 OK
After Getting Response from Play method i start sending my encoded data using send method in Rtsp client.
But the audio is not audible at camera end. My question is simple-
Is it is possible to send audio data over RTSP URL.
Is there any issue with the way I called my methods which is wrong?could any one please point that out.
Is there any easy way (or example/tutorial) that shows how to do audio backchannel over rtsp please provide it to.
Please don't mind I am new to Rtsp. Thanks in advance. If any issue with the question tell me I will edit it for move clear understanding.
I figured it out what I was doing wrong. Actually above mentioned steps are fine and camera returning Ok, what I was doing wrong was here in above code :
sourceStream.WaveFormat = new WaveFormat(64, 8, 1); //8000 16
Instead of 64 & 8 parameter it should be :
sourceStream.WaveFormat = new WaveFormat(8000, 16, 1); //8000 16
It was all voice smapling rate due to which voice send was not audible. Thankyou!
Related
I have an outgoing POST from another application, and with the help of Fiddler I want to send another order opening request. I managed to take the necessary data from the original request, sign the request using HMAC256 and send it to Binance, but I get a "bad request" in the response and I don't understand what's wrong here. I would appreciate any help.
public static void OnBeforeResponse(Session oSession)
{
if (oSession.HTTPMethodIs("POST") && oSession.uriContains("order"))
{
String strBody = oSession.GetRequestBodyAsString();
//Price
int PriceStart = strBody.IndexOf("price=") + 6;
int PriceEND = strBody.IndexOf("&", PriceStart);
string priceStr = strBody.Substring(PriceStart, PriceEND - PriceStart);
float priceF = float.Parse(priceStr, System.Globalization.CultureInfo.InvariantCulture);
// SYMBOL
int symbolStart = strBody.IndexOf("symbol=") + 7;
int symbolend = strBody.IndexOf("BTC", symbolStart);
string symbol = strBody.Substring(symbolStart, symbolend - symbolStart);
// Quantity
int quantStart = strBody.IndexOf("quantity=") + 9;
int quantend = strBody.IndexOf("&price", quantStart);
string quant = strBody.Substring(quantStart, quantend - quantStart);
float quantity = float.Parse(quant, System.Globalization.CultureInfo.InvariantCulture) * 2;
// timestamp
decimal timestamp = Math.Round(Convert.ToDecimal(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds), 0);
//Sign
Encoding ascii = Encoding.ASCII;
string secretKey = "lfaeGkjitNwvyG2lqDueMhSAOzRFlzL73w5pKRCAvSy7YrxyTkvwKCcHBHj...";
HMACSHA256 hmac = new HMACSHA256(ascii.GetBytes(secretKey));
// string query_string_LIMIT = "symbol="+symbol+"USDT&side=BUY&type=LIMIT&timeInForce=GTC&quantity="+quantity+"&price="+priceF+"&recvWindow=5000×tamp="+timestamp+"&signature=";
string result = "symbol=" + symbol + "USDT&side=BUY&type=MARKET&quantity=" + quantity + "&recvWindow=5000×tamp=" + timestamp + "&signature=";
String signature = BitConverter.ToString(hmac.ComputeHash(ascii.GetBytes(result))).Replace("-", "");
oSession.host = "fapi.binance.com";
string resultRequest = "symbol=" + symbol + "USDT&side=BUY&type=MARKET&quantity=" + quantity + "&recvWindow=5000×tamp=" + timestamp + "&signature=" + signature;
byte[] resulByte = System.Text.Encoding.ASCII.GetBytes(resultRequest);
//oSession.utilReplaceInRequest("api/v3/order","fapi/v1/order?"+resultFin);
oSession.url = oSession.url.Replace("api/v3/order", "fapi/v1/order?" + resultRequest);
FiddlerApplication.oProxy.SendRequest(oSession.RequestHeaders, resulByte, null);
}
}
I am trying write a function that takes a single IP address as a parameter and queries that machine on my local network for it's MAC address.
I have seen many examples that get the local machine's own MAC address, however none (I've found) that seem to query a local network machine for it.
I know such a task is achievable as this Wake on LAN scanner software scans the local IP range and returns MAC address/Hostname of all on machines.
Can anyone tell me where I'd get started trying to write a function to achieve this in C#? Any help would be appreciated. Thanks
EDIT:
As per Marco Mp's comment below, have used ARP tables.
arp class
public string GetMacAddress(string ipAddress)
{
string macAddress = string.Empty;
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "arp";
pProcess.StartInfo.Arguments = "-a " + ipAddress;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.CreateNoWindow = true;
pProcess.Start();
string strOutput = pProcess.StandardOutput.ReadToEnd();
string[] substrings = strOutput.Split('-');
if (substrings.Length >= 8)
{
macAddress = substrings[3].Substring(Math.Max(0, substrings[3].Length - 2))
+ "-" + substrings[4] + "-" + substrings[5] + "-" + substrings[6]
+ "-" + substrings[7] + "-"
+ substrings[8].Substring(0, 2);
return macAddress;
}
else
{
return "not found";
}
}
Very late edit:
In open souce project iSpy (https://github.com/ispysoftware/iSpy) they use this code, which is a little nicer
public static void RefreshARP()
{
_arpList = new Dictionary<string, string>();
_arpList.Clear();
try
{
var arpStream = ExecuteCommandLine("arp", "-a");
// Consume first three lines
for (int i = 0; i < 3; i++)
{
arpStream.ReadLine();
}
// Read entries
while (!arpStream.EndOfStream)
{
var line = arpStream.ReadLine();
if (line != null)
{
line = line.Trim();
while (line.Contains(" "))
{
line = line.Replace(" ", " ");
}
var parts = line.Trim().Split(' ');
if (parts.Length == 3)
{
string ip = parts[0];
string mac = parts[1];
if (!_arpList.ContainsKey(ip))
_arpList.Add(ip, mac);
}
}
}
}
catch (Exception ex)
{
Logger.LogExceptionToFile(ex, "ARP Table");
}
if (_arpList.Count > 0)
{
foreach (var nd in List)
{
string mac;
ARPList.TryGetValue(nd.IPAddress.ToString(), out mac);
nd.MAC = mac;
}
}
}
https://github.com/ispysoftware/iSpy/blob/master/Server/NetworkDeviceList.cs
Update 2 even more late, but I think is best because it uses regex that checks better for exact matches.
public string getMacByIp(string ip)
{
var macIpPairs = GetAllMacAddressesAndIppairs();
int index = macIpPairs.FindIndex(x => x.IpAddress == ip);
if (index >= 0)
{
return macIpPairs[index].MacAddress.ToUpper();
}
else
{
return null;
}
}
public List<MacIpPair> GetAllMacAddressesAndIppairs()
{
List<MacIpPair> mip = new List<MacIpPair>();
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "arp";
pProcess.StartInfo.Arguments = "-a ";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.CreateNoWindow = true;
pProcess.Start();
string cmdOutput = pProcess.StandardOutput.ReadToEnd();
string pattern = #"(?<ip>([0-9]{1,3}\.?){4})\s*(?<mac>([a-f0-9]{2}-?){6})";
foreach (Match m in Regex.Matches(cmdOutput, pattern, RegexOptions.IgnoreCase))
{
mip.Add(new MacIpPair()
{
MacAddress = m.Groups["mac"].Value,
IpAddress = m.Groups["ip"].Value
});
}
return mip;
}
public struct MacIpPair
{
public string MacAddress;
public string IpAddress;
}
using System.Net;
using System.Runtime.InteropServices;
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(int DestIP, int SrcIP, [Out] byte[] pMacAddr, ref int PhyAddrLen);
try
{
IPAddress hostIPAddress = IPAddress.Parse("XXX.XXX.XXX.XX");
byte[] ab = new byte[6];
int len = ab.Length,
r = SendARP((int)hostIPAddress.Address, 0, ab, ref len);
Console.WriteLine(BitConverter.ToString(ab, 0, 6));
}
catch (Exception ex) { }
or with PC Name
try
{
Tempaddr = System.Net.Dns.GetHostEntry("DESKTOP-xxxxxx");
}
catch (Exception ex) { }
byte[] ab = new byte[6];
int len = ab.Length, r = SendARP((int)Tempaddr.AddressList[1].Address, 0, ab, ref len);
Console.WriteLine(BitConverter.ToString(ab, 0, 6));
Just a better performing version of the accepted method.
public string GetMacByIp( string ip )
{
var pairs = this.GetMacIpPairs();
foreach( var pair in pairs )
{
if( pair.IpAddress == ip )
return pair.MacAddress;
}
throw new Exception( $"Can't retrieve mac address from ip: {ip}" );
}
public IEnumerable<MacIpPair> GetMacIpPairs()
{
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "arp";
pProcess.StartInfo.Arguments = "-a ";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.CreateNoWindow = true;
pProcess.Start();
string cmdOutput = pProcess.StandardOutput.ReadToEnd();
string pattern = #"(?<ip>([0-9]{1,3}\.?){4})\s*(?<mac>([a-f0-9]{2}-?){6})";
foreach( Match m in Regex.Matches( cmdOutput, pattern, RegexOptions.IgnoreCase ) )
{
yield return new MacIpPair()
{
MacAddress = m.Groups[ "mac" ].Value,
IpAddress = m.Groups[ "ip" ].Value
};
}
}
public struct MacIpPair
{
public string MacAddress;
public string IpAddress;
}
With SharpPCap, It will work on Windows & Linux
public string getMacAdress(string ip){
LibPcapLiveDeviceList devices = LibPcapLiveDeviceList.Instance;//list all your network cards
ARP arp = new ARP(devices[0]);//select the first network card by default
IPAddress ip = IPAddress.Parse(ip);
PhysicalAddress macAdress = arp.Resolve(ip);
return macAdress.ToString();
}
As per Marco Mp's comment above, have used ARP tables. arp class
I have an app which pings IP or IP range. The problem is that when hosts are closed it takes longer to ping than they are open. When host is closed the time to ping is about 1-2 seconds.
How could I make it faster when hosts are closed?
This is my code:
using System;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
namespace Range_Pinger
{
public partial class PingIPRange : Form
{
uint startIP, endIP, currentIP;
int count = 0;
int open = 0;
int closed = 0;
public PingIPRange()
{
InitializeComponent();
tmrPingInterval.Tick += new EventHandler(tmrPingInterval_Tick);
}
void tmrPingInterval_Tick(object sender, EventArgs e)
{
if (txtTo.Text == string.Empty) Ping(ip2str(startIP));
else
{
if (currentIP >= endIP) tmrPingInterval.Stop();
Ping(ip2str(currentIP));
currentIP++;
}
count++;
tsslPingCount.Text = "Total number of pings: " + count.ToString() +
" Open IPs: " + open.ToString() + " Closed IPs: " + closed.ToString();
}
static uint str2ip(string ip)
{
string[] numbers = ip.Split('.');
uint x1 = (uint)(Convert.ToByte(numbers[0]) << 24);
uint x2 = (uint)(Convert.ToByte(numbers[1]) << 16);
uint x3 = (uint)(Convert.ToByte(numbers[2]) << 8);
uint x4 = (uint)(Convert.ToByte(numbers[3]));
return x1 + x2 + x3 + x4;
}
static string ip2str(uint ip)
{
string s1 = ((ip & 0xff000000) >> 24).ToString() + ".";
string s2 = ((ip & 0x00ff0000) >> 16).ToString() + ".";
string s3 = ((ip & 0x0000ff00) >> 8).ToString() + ".";
string s4 = (ip & 0x000000ff).ToString();
return s1 + s2 + s3 + s4;
}
private void btnPing_Click(object sender, EventArgs e)
{
txtDisplay.Text = string.Empty;
tsslPingCount.Text = string.Empty;
count = 0;
open = 0;
closed = 0;
tmrPingInterval.Interval = int.Parse(nudInterval.Value.ToString());
try
{
startIP = str2ip(txtFrom.Text);
if (txtTo.Text != string.Empty) endIP = str2ip(txtTo.Text);
currentIP = startIP;
tmrPingInterval.Start();
}
catch
{
MessageBox.Show("Invalid input. It must be something like: 255.255.255.255");
}
}
private void btnStop_Click(object sender, EventArgs e)
{
tmrPingInterval.Stop();
}
private void Ping(string address)
{
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
string data = "01234567890123456789012345678901";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
try
{
PingReply reply = pingSender.Send(address, timeout, buffer, options) ;
if (reply.Status == IPStatus.Success)
{
open++;
txtDisplay.AppendText("Host " + address + " is open." + Environment.NewLine);
}
else
{
closed++;
txtDisplay.AppendText("Host " + address + " is closed." + Environment.NewLine);
}
}
catch (Exception ex)
{
txtDisplay.SelectedText += Environment.NewLine + ex.Message;
}
}
private void tsmiExit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
This is what I have now:
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(IPAddress DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
private void Ping(IPAddress address)
{
byte[] macAddr = new byte[6];
uint macAddrLen = (uint)macAddr.Length;
if (SendARP(address, 0, macAddr, ref macAddrLen) == 0)
{
txtDisplay.AppendText("Host " + address + " is open." + Environment.NewLine);
}
else txtDisplay.AppendText("Host " + address + " is closed." + Environment.NewLine);
}
You shouldn't reduce the timeout. Try to send multiple pings at once async.
var ping = new Ping();
ping.PingCompleted += (sender, eventArgs) =>
{
// eventArgs.Reply.Address
// eventArgs.Reply.Status
};
ping.SendAsync(ip, etc.);
Your address is a string. Thus it will go via DNS first to see if this is possibly a hostname (even if it is an IP address).
I suggest you use the overload taking an IPAddress instead.
I created a live host scanner not too long ago. It uses ARP to check if a computer is online.
An ARP request is much faster than if you'd ping a host.
Here's the code I used to check if a Host is available:
//You'll need this pinvoke signature as it is not part of the .Net framework
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(int DestIP, int SrcIP,
byte[] pMacAddr, ref uint PhyAddrLen);
//These vars are needed, if the the request was a success
//the MAC address of the host is returned in macAddr
private byte[] macAddr = new byte[6];
private uint macAddrLen;
//Here you can put the IP that should be checked
private IPAddress Destination = IPAddress.Parse("127.0.0.1");
//Send Request and check if the host is there
if (SendARP((int)Destination.Address, 0, macAddr, ref macAddrLen) == 0)
{
//SUCCESS! Igor it's alive!
}
If you're interested Nmap also uses this technique to scan for available hosts.
ARP scan puts Nmap and its optimized algorithms in charge of ARP requests. And if it gets a response back, Nmap doesn't even need to worry about the IP-based ping packets since it already knows the host is up. This makes ARP scan much faster and more reliable than IP-based scans. So it is done by default when scanning ethernet hosts that Nmap detects are on a local ethernet network. Even if different ping types (such as -PE or -PS) are specified, Nmap uses ARP instead for any of the targets which are on the same LAN.
EDIT:
This only works within the current subnet! As long as there is no router between the requesting machine and the target it should work fine.
ARP is a non-routable protocol, and can therefore only be used between systems on the same Ethernet network. [...]
arp-scan can be used to discover IP hosts on the local network. It can discover all hosts, including those that block all IP traffic such as firewalls and systems with ingress filters. - Excerpt from NTA-Monitor wiki
For more information on the SendARP function you can check the pinvoke.net documentation.
You need to redesign your application to use multithreading -> tasks. Issue a task for each ping, and when you receive a response from a given host fire an event and update the UI. Changing socket timeout will only help you to reduce the timeout from outrageous to insufferable.
Not sure if this is any help (see final post on the thread), it seems an almost identical problem. What you're butting up against there is the protocol stack's timeout. You can get around it if you use socket to connect as you'll have more control.
In .net, I can create a NTAccount using domain and username, and get it's SID.
But I cannot convert the SID back to NTAccount using translate function.
new SecurityIdentifier(stringSid).Translate(typeof(NTAccount)).ToString();
And this two way conversion code has no problem running on Domain Controller.
Maybe some configuration wrong?
SecurityIdentifier.Translate() method works only on domain accounts so perhaps your computer not attached to domain. To resolve local SIDs into account name you can use Win32 API function LookupAccountSid() look here for example.
Instead of using the SecurityIdentifier, you can use an easier and more general use of DirectoryServices in .NET.
In codeproject, there is a nice sample of this:
http://www.codeproject.com/KB/cs/getusersid.aspx
The code is:
private string GetSid(string strLogin)
{
string str = "";
// Parse the string to check if domain name is present.
int idx = strLogin.IndexOf('\\');
if (idx == -1)
{
idx = strLogin.IndexOf('#');
}
string strDomain;
string strName;
if (idx != -1)
{
strDomain = strLogin.Substring(0, idx);
strName = strLogin.Substring(idx+1);
}
else
{
strDomain = Environment.MachineName;
strName = strLogin;
}
DirectoryEntry obDirEntry = null;
try
{
Int64 iBigVal = 5;
Byte[] bigArr = BitConverter.GetBytes(iBigVal);
obDirEntry = new DirectoryEntry("WinNT://" +
strDomain + "/" + strName);
System.DirectoryServices.PropertyCollection
coll = obDirEntry.Properties;
object obVal = coll["objectSid"].Value;
if (null != obVal)
{
str = this.ConvertByteToStringSid((Byte[])obVal);
}
}
catch (Exception ex)
{
str = "";
Trace.Write(ex.Message);
}
return str;
}
private string ConvertByteToStringSid(Byte[] sidBytes)
{
StringBuilder strSid = new StringBuilder();
strSid.Append("S-");
try
{
// Add SID revision.
strSid.Append(sidBytes[0].ToString());
// Next six bytes are SID authority value.
if (sidBytes[6] != 0 || sidBytes[5] != 0)
{
string strAuth = String.Format
("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
(Int16)sidBytes[1],
(Int16)sidBytes[2],
(Int16)sidBytes[3],
(Int16)sidBytes[4],
(Int16)sidBytes[5],
(Int16)sidBytes[6]);
strSid.Append("-");
strSid.Append(strAuth);
}
else
{
Int64 iVal = (Int32)(sidBytes[1]) +
(Int32)(sidBytes[2] << 8) +
(Int32)(sidBytes[3] << 16) +
(Int32)(sidBytes[4] << 24);
strSid.Append("-");
strSid.Append(iVal.ToString());
}
// Get sub authority count...
int iSubCount = Convert.ToInt32(sidBytes[7]);
int idxAuth = 0;
for (int i = 0; i < iSubCount; i++)
{
idxAuth = 8 + i * 4;
UInt32 iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
strSid.Append("-");
strSid.Append(iSubAuth.ToString());
}
}
catch (Exception ex)
{
Trace.Warn(ex.Message);
return "";
}
return strSid.ToString();
}
There is also a conversion from SID bytes to String in the article.
Working in C# with the EWS Managed API, we're having trouble efficiently retrieving the images stored as inline attachments.
The endpoint is to show an email with inline images as a fully formed html page in a panel. The code we currently us:
string sHTMLCOntent = item.Body;
FileAttachment[] attachments = null;
if (item.Attachments.Count != 0)
{
attachments = new FileAttachment[item.Attachments.Count];
for (int i = 0; i < item.Attachments.Count; i++)
{
string sType = item.Attachments[i].ContentType.ToLower();
if (sType.Contains("image"))
{
attachments[i] = (FileAttachment)item.Attachments[i];
string sID = attachments[i].ContentId;
sType = sType.Replace("image/", "");
string sFilename = sID + "." + sType;
string sPathPlusFilename = Directory.GetCurrentDirectory() + "\\" + sFilename;
attachments[i].Load(sFilename);
string oldString = "cid:" + sID;
sHTMLCOntent = sHTMLCOntent.Replace(oldString, sPathPlusFilename);
}
}
}
(sourced: http://social.technet.microsoft.com/Forums/en-US/exchangesvrdevelopment/thread/ad10283a-ea04-4b15-b20a-40cbd9c95b57)
.. this is not very efficient though and is slowing down the responsiveness of our web app. Does anyone have a better solution for this problem? We are using Exchange 2007 SP1, so the IsInline property wont work as its Exchange 2010 only.
I build an index of your "cid:"s first:
private const string CidPattern = "cid:";
private static HashSet<int> BuildCidIndex(string html)
{
var index = new HashSet<int>();
var pos = html.IndexOf(CidPattern, 0);
while (pos > 0)
{
var start = pos + CidPattern.Length;
index.Add(start);
pos = html.IndexOf(CidPattern, start);
}
return index;
}
Then you need a replace function that replaces the cids based on your index
private static void AdjustIndex(HashSet<int> index, int oldPos, int byHowMuch)
{
var oldIndex = new List<int>(index);
index.Clear();
foreach (var pos in oldIndex)
{
if (pos < oldPos)
index.Add(pos);
else
index.Add(pos + byHowMuch);
}
}
private static bool ReplaceCid(HashSet<int> index, ref string html, string cid, string path)
{
var posToRemove = -1;
foreach (var pos in index)
{
if (pos + cid.Length < html.Length && html.Substring(pos, cid.Length) == cid)
{
var sb = new StringBuilder();
sb.Append(html.Substring(0, pos-CidPattern.Length));
sb.Append(path);
sb.Append(html.Substring(pos + cid.Length));
html = sb.ToString();
posToRemove = pos;
break;
}
}
if (posToRemove < 0)
return false;
index.Remove(posToRemove);
AdjustIndex(index, posToRemove, path.Length - (CidPattern.Length + cid.Length));
return true;
}
so now, you can check your attachments
FileAttachment[] attachments = null;
var index = BuildCidIndex(sHTMLCOntent);
if (index.Count > 0 && item.Attachments.Count > 0)
{
var basePath = Directory.GetCurrentDirectory();
attachments = new FileAttachment[item.Attachments.Count];
for (var i = 0; i < item.Attachments.Count; ++i)
{
var type = item.Attachments[i].ContentType.ToLower();
if (!type.StartsWith("image/")) continue;
type = type.Replace("image/", "");
var attachment = (FileAttachment)item.Attachments[i];
var cid = attachment.ContentId;
var filename = cid + "." + type;
var path = Path.Combine(basePath, filename);
if(ReplaceCid(index, ref sHTMLCOntent, cid, path))
{
// only load images when they have been found
attachment.Load(path);
attachments[i] = attachment;
}
}
}
Additional to that: instead of calling attachment.Load right away, and pass the path to the image directly, you could link to another script, where you pass the cid as a parameter and then check back with the exchange for that image; then the process of loading the image from exchange does not block the html cid replacement and could lead to loading the page faster, since the html can send to the browser sooner.
PS: Code is not tested, just so you get the idea!
EDIT
Added the missing AdjustIndex function.
EDIT 2
Fixed small bug in AdjustIndex