Skip to content

Commit

Permalink
Improve authentication digest and http transport
Browse files Browse the repository at this point in the history
  • Loading branch information
ngraziano committed Feb 3, 2024
1 parent 4f63037 commit 8496c29
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 57 deletions.
43 changes: 20 additions & 23 deletions RTSP/AuthenticationDigest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,41 @@ public override bool IsValid(RtspMessage received_message)
// Check Username, URI, Nonce and the MD5 hashed Response
if (authorization != null && authorization.StartsWith("Digest ", StringComparison.Ordinal))
{
string value_str = authorization.Substring(7); // remove 'Digest '
string[] values = value_str.Split(',');
// remove 'Digest '
var value_str = authorization[7..];
string? auth_header_username = null;
string? auth_header_realm = null;
string? auth_header_nonce = null;
string? auth_header_uri = null;
string? auth_header_response = null;

foreach (string value in values)
foreach (string value in value_str.Split(','))
{
string[] tuple = value.Trim().Split(new char[] { '=' }, 2); // split on first '='
if (tuple.Length == 2 && tuple[0].Equals("username", StringComparison.OrdinalIgnoreCase))
string[] tuple = value.Trim().Split('=', 2);
if (tuple.Length != 2)
{
auth_header_username = tuple[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
continue;
}
else if (tuple.Length == 2 && tuple[0].Equals("realm", StringComparison.OrdinalIgnoreCase))
string var = tuple[1].Trim([' ', '\"']);
if (tuple[0].Equals("username", StringComparison.OrdinalIgnoreCase))
{
auth_header_realm = tuple[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
auth_header_username = var;
}
else if (tuple.Length == 2 && tuple[0].Equals("nonce", StringComparison.OrdinalIgnoreCase))
else if (tuple[0].Equals("realm", StringComparison.OrdinalIgnoreCase))
{
auth_header_nonce = tuple[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
auth_header_realm = var;
}
else if (tuple.Length == 2 && tuple[0].Equals("uri", StringComparison.OrdinalIgnoreCase))
else if (tuple[0].Equals("nonce", StringComparison.OrdinalIgnoreCase))
{
auth_header_uri = tuple[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
auth_header_nonce = var;
}
else if (tuple.Length == 2 && tuple[0].Equals("response", StringComparison.OrdinalIgnoreCase))
else if (tuple[0].Equals("uri", StringComparison.OrdinalIgnoreCase))
{
auth_header_response = tuple[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
auth_header_uri = var;
}
else if (tuple[0].Equals("response", StringComparison.OrdinalIgnoreCase))
{
auth_header_response = var;
}
}

Expand All @@ -116,15 +121,7 @@ public override bool IsValid(RtspMessage received_message)
private static string CalculateMD5Hash(MD5 md5_session, string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] hash = md5_session.ComputeHash(inputBytes);

var output = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
output.Append(hash[i].ToString("x2"));
}

return output.ToString();
return CalculateMD5Hash(md5_session, inputBytes);
}
private static string CalculateMD5Hash(MD5 md5_session, byte[] input)
{
Expand Down
4 changes: 2 additions & 2 deletions RTSP/IRTSPTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public interface IRtspTransport
string RemoteAddress { get; }

/// <summary>
/// Keep track of issued commands...
/// Get next command index. Increment at each call.
/// </summary>
uint CommandCounter { get; }
uint NextCommandIndex();

/// <summary>
/// Closes this instance.
Expand Down
3 changes: 0 additions & 3 deletions RTSP/RTSP.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,4 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Utils\" />
</ItemGroup>
</Project>
50 changes: 32 additions & 18 deletions RTSP/RTSPHttpTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Rtsp
{
public class RtspHttpTransport : IRtspTransport
public class RtspHttpTransport : IRtspTransport, IDisposable
{
private readonly NetworkCredential _credentials;
private readonly Uri _uri;
Expand All @@ -25,6 +25,7 @@ public class RtspHttpTransport : IRtspTransport

private uint _commandCounter = 0;
private string _sessionCookie = string.Empty;
private bool disposedValue;

public RtspHttpTransport(Uri uri, NetworkCredential credentials)
{
Expand All @@ -37,7 +38,7 @@ public RtspHttpTransport(Uri uri, NetworkCredential credentials)
public string RemoteAddress => $"{_uri}";
public bool Connected => _streamDataClient != null && _streamDataClient.Connected;

public uint CommandCounter => ++_commandCounter;
public uint NextCommandIndex() => ++_commandCounter;

public void Close()
{
Expand Down Expand Up @@ -120,19 +121,19 @@ public void Write(byte[] buffer, int offset, int count)
string request = ComposePostRequest(base64CommandBytes);
byte[] requestBytes = Encoding.ASCII.GetBytes(request);

ArraySegment<byte>[] sendList = new ArraySegment<byte>[]
{
ArraySegment<byte>[] sendList =
[
new(requestBytes),
new(base64CommandBytes)
};
new(base64CommandBytes),
];

_commandsClient.Send(sendList, SocketFlags.None);
}
}

private string ComposeGetRequest()
{
string authorizationHeader = GetAuthorizationHeader(CommandCounter, "GET", Array.Empty<byte>());
string authorizationHeader = GetAuthorizationHeader(NextCommandIndex(), "GET", []);

StringBuilder sb = new();
sb.AppendLine($"GET {_uri.PathAndQuery} HTTP/1.0");
Expand All @@ -148,7 +149,7 @@ private string ComposeGetRequest()

private string ComposePostRequest(byte[] commandBytes)
{
string authorizationHeader = GetAuthorizationHeader(CommandCounter, "POST", commandBytes);
string authorizationHeader = GetAuthorizationHeader(NextCommandIndex(), "POST", commandBytes);

StringBuilder sb = new();
sb.AppendLine($"POST {_uri.PathAndQuery} HTTP/1.0");
Expand All @@ -167,19 +168,13 @@ private string ComposePostRequest(byte[] commandBytes)

private string GetAuthorizationHeader(uint counter, string method, byte[] requestBytes)
{
string authorizationHeader;

if (_authentication != null)
{
string headerValue = _authentication.GetResponse(counter, _uri.PathAndQuery, method, requestBytes);
authorizationHeader = $"Authorization: {headerValue}\r\n";
}
else
if (_authentication == null)
{
authorizationHeader = string.Empty;
return string.Empty;
}

return authorizationHeader;
string headerValue = _authentication.GetResponse(counter, _uri.PathAndQuery, method, requestBytes);
return $"Authorization: {headerValue}\r\n";
}

private static int ReadUntilEndOfHeaders(Stream stream, byte[] buffer, int length)
Expand Down Expand Up @@ -216,5 +211,24 @@ private static int ReadUntilEndOfHeaders(Stream stream, byte[] buffer, int lengt

return totalRead;
}

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Close();
}
disposedValue = true;
}
}

public void Dispose()
{
// Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)'
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
2 changes: 1 addition & 1 deletion RTSP/RTSPTCPTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public RtspTcpTransport(Uri uri)
/// <value>The remote address.</value>
public string RemoteAddress => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", _currentEndPoint.Address, _currentEndPoint.Port);

public uint CommandCounter => ++_commandCounter;
public uint NextCommandIndex() => ++_commandCounter;

/// <summary>
/// Closes this instance.
Expand Down
35 changes: 25 additions & 10 deletions RtspClientExample/RTSPClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void Connect(string url, string username, string password, RTP_TRANSPORT
return;
}

if (rtspSocket.Connected == false)
if (!rtspSocket.Connected)
{
rtspSocketStatus = RTSP_STATUS.ConnectFailed;
_logger.LogWarning("Error - did not connect");
Expand Down Expand Up @@ -189,38 +189,53 @@ public void Connect(string url, string username, string password, RTP_TRANSPORT

public void Pause()
{
if(rtspSocket is null || _uri is null)
{
throw new InvalidOperationException("Not connected");
}

// Send PAUSE
RtspRequest pause_message = new RtspRequestPause
{
RtspUri = _uri,
Session = session
};
pause_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
pause_message.AddAuthorization(_authentication, _uri, rtspSocket.NextCommandIndex());
rtspClient?.SendMessage(pause_message);
}

public void Play()
{
if (rtspSocket is null || _uri is null)
{
throw new InvalidOperationException("Not connected");
}

// Send PLAY
RtspRequest play_message = new RtspRequestPlay
{
RtspUri = _uri,
Session = session
};
play_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
play_message.AddAuthorization(_authentication, _uri, rtspSocket.NextCommandIndex());
rtspClient?.SendMessage(play_message);
}


public void Stop()
{
if (rtspSocket is null || _uri is null)
{
throw new InvalidOperationException("Not connected");
}

// Send TEARDOWN
RtspRequest teardown_message = new RtspRequestTeardown
{
RtspUri = _uri,
Session = session
};
teardown_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
teardown_message.AddAuthorization(_authentication, _uri, rtspSocket.NextCommandIndex());
rtspClient?.SendMessage(teardown_message);

// Stop the keepalive timer
Expand Down Expand Up @@ -560,7 +575,7 @@ private void RtspMessageReceived(object? sender, RtspChunkEventArgs e)

if (resend_message is not null)
{
resend_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
resend_message.AddAuthorization(_authentication, _uri!, rtspSocket!.NextCommandIndex());
rtspClient?.SendMessage(resend_message);
}
return;
Expand Down Expand Up @@ -602,7 +617,7 @@ private void RtspMessageReceived(object? sender, RtspChunkEventArgs e)
{
RtspUri = _uri
};
describe_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
describe_message.AddAuthorization(_authentication, _uri!, rtspSocket!.NextCommandIndex());
rtspClient?.SendMessage(describe_message);
}
else
Expand Down Expand Up @@ -732,7 +747,7 @@ private void RtspMessageReceived(object? sender, RtspChunkEventArgs e)
// Need for old sony camera SNC-CS20
play_message.Headers.Add("range", "npt=0.000-");

play_message.AddAuthorization(_authentication, _uri!, rtspSocket!.CommandCounter);
play_message.AddAuthorization(_authentication, _uri!, rtspSocket!.NextCommandIndex());
rtspClient?.SendMessage(play_message);
}
}
Expand Down Expand Up @@ -901,7 +916,7 @@ private void HandleDescribeResponse(RtspResponse message)
RtspUri = video_uri
};
setup_message.AddTransport(transport);
setup_message.AddAuthorization(_authentication!, _uri!, rtspSocket!.CommandCounter);
setup_message.AddAuthorization(_authentication!, _uri!, rtspSocket!.NextCommandIndex());

// Add SETUP message to list of mesages to send
setupMessages.Enqueue(setup_message);
Expand Down Expand Up @@ -963,7 +978,7 @@ private void HandleDescribeResponse(RtspResponse message)
RtspUri = audio_uri,
};
setup_message.AddTransport(transport);
setup_message.AddAuthorization(_authentication!, _uri!, rtspSocket!.CommandCounter);
setup_message.AddAuthorization(_authentication!, _uri!, rtspSocket!.NextCommandIndex());

// Add SETUP message to list of mesages to send
setupMessages.Enqueue(setup_message);
Expand Down Expand Up @@ -1061,7 +1076,7 @@ void SendKeepAlive(object? sender, System.Timers.ElapsedEventArgs e)
};


keepAliveMessage.AddAuthorization(_authentication!, _uri!, rtspSocket!.CommandCounter);
keepAliveMessage.AddAuthorization(_authentication!, _uri!, rtspSocket!.NextCommandIndex());
rtspClient?.SendMessage(keepAliveMessage);
}

Expand Down

0 comments on commit 8496c29

Please sign in to comment.