private bool SendAuthenticationChallenge(RtspProtocol from, Variant realm) { //10. Ok, the user doesn't know that this needs authentication. We //will respond back with a nice 401. Generate the line first string wwwAuthenticate = HTTPAuthHelper.GetWWWAuthenticateHeader( realm["method"], realm["name"]); //12. Save the nonce for later validation when new requests are coming in again from.CustomParameters["wwwAuthenticate"] = wwwAuthenticate; //13. send the response from.PushResponseFirstLine(RTSP_VERSION_1_0, 401, "Unauthorized"); from.PushResponseHeader(HTTP_HEADERS_WWWAUTHENTICATE, wwwAuthenticate); return from.SendResponseMessage(); }
private bool HandleRTSPRequestAnnounce(RtspProtocol pFrom, Variant requestHeaders, string requestContent) { //1. Make sure we ONLY handle application/sdp if ((string)requestHeaders[RTSP_HEADERS,RTSP_HEADERS_CONTENT_TYPE]!= RTSP_HEADERS_ACCEPT_APPLICATIONSDP) { FATAL("Invalid ANNOUNCE request:\n{0}", (requestHeaders.ToString())); return false; } //2. Get the SDP var sdp = pFrom.InboundSDP; //3. Parse the SDP if (!SDP.ParseSDP(sdp, requestContent)) { FATAL("Unable to parse the SDP"); return false; } //4. Get the first video track var videoTrack = sdp.GetVideoTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]); var audioTrack = sdp.GetAudioTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]); //5. Store the tracks inside the session for later use if (audioTrack != VariantType.Null) { pFrom.CustomParameters["pendingTracks",audioTrack["globalTrackIndex"]] = audioTrack; } if (videoTrack != VariantType.Null) { pFrom.CustomParameters["pendingTracks",videoTrack["globalTrackIndex"]] = videoTrack; } //6. Mark this connection as inbound connection pFrom.CustomParameters["isInbound"] = true; //7. Save the streamName string streamName = sdp.GetStreamName(); if (streamName == "") { streamName = $"rtsp_stream_{pFrom.Id}"; } pFrom.CustomParameters["sdpStreamName"] = streamName; streamName = new Uri(requestHeaders[RTSP_FIRST_LINE, RTSP_URL],UriKind.Absolute).Segments.Last(); //8. Save the bandwidth hint pFrom.CustomParameters["sdpBandwidthHint"] = sdp.GetTotalBandwidth(); //9. Get the inbound connectivity InboundConnectivity pInboundConnectivity = pFrom.GetInboundConnectivity( streamName, sdp.GetTotalBandwidth(),Application.Configuration[CONF_APPLICATION_RTCPDETECTIONINTERVAL]); if (pInboundConnectivity == null) { FATAL("Unable to create inbound connectivity"); return false; } //8. Send back the response pFrom.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); return pFrom.SendResponseMessage(); }
private bool HandleRTSPRequestPlay(RtspProtocol pFrom, Variant requestHeaders, string requestContent) { //1. Get the outbound connectivity bool forceTcp = pFrom.CustomParameters["forceTcp"]; var pOutboundConnectivity = GetOutboundConnectivity(pFrom, true); if (pOutboundConnectivity == null) { FATAL("Unable to get the outbound connectivity"); return false; } if (forceTcp) { //3. Get the audio/video client ports byte videoDataChannelNumber = 0xff; byte videoRtcpChannelNumber = 0xff; byte audioDataChannelNumber = 0xff; byte audioRtcpChannelNumber = 0xff; if (pFrom.CustomParameters["audioDataChannelNumber"]!=null) { audioDataChannelNumber = pFrom.CustomParameters["audioDataChannelNumber"]; audioRtcpChannelNumber = pFrom.CustomParameters["audioRtcpChannelNumber"]; } if (pFrom.CustomParameters["videoDataChannelNumber"] != null) { videoDataChannelNumber = pFrom.CustomParameters["videoDataChannelNumber"]; videoRtcpChannelNumber = pFrom.CustomParameters["videoRtcpChannelNumber"]; } //4.register the video if (videoDataChannelNumber != 0xff) { if (!pOutboundConnectivity.RegisterTCPVideoClient(pFrom.Id, videoDataChannelNumber, videoRtcpChannelNumber)) { FATAL("Unable to register video stream"); return false; } } //5. Register the audio if (audioDataChannelNumber != 0xff) { if (!pOutboundConnectivity.RegisterTCPAudioClient(pFrom.Id, audioDataChannelNumber, audioRtcpChannelNumber)) { FATAL("Unable to register audio stream"); return false; } } } else { //3. Get the audio/video client ports ushort videoDataPortNumber = 0; ushort videoRtcpPortNumber = 0; ushort audioDataPortNumber = 0; ushort audioRtcpPortNumber = 0; if (pFrom.CustomParameters["audioDataPortNumber"] != null) { audioDataPortNumber = pFrom.CustomParameters["audioDataPortNumber"]; audioRtcpPortNumber = pFrom.CustomParameters["audioRtcpPortNumber"]; } if (pFrom.CustomParameters["videoDataPortNumber"] != null) { videoDataPortNumber = pFrom.CustomParameters["videoDataPortNumber"]; videoRtcpPortNumber = pFrom.CustomParameters["videoRtcpPortNumber"]; } //4.register the video if (videoDataPortNumber != 0) { var videoDataAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo; videoDataAddress.Port = videoDataPortNumber; var videoRtcpAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo; videoRtcpAddress.Port = videoRtcpPortNumber; if (!pOutboundConnectivity.RegisterUDPVideoClient(pFrom.Id, videoDataAddress, videoRtcpAddress)) { FATAL("Unable to register video stream"); return false; } } //5. Register the audio if (audioDataPortNumber != 0) { var audioDataAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo; audioDataAddress.Port = audioDataPortNumber; var audioRtcpAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo; audioRtcpAddress.Port = audioRtcpPortNumber; if (!pOutboundConnectivity.RegisterUDPAudioClient(pFrom.Id,audioDataAddress, audioRtcpAddress)) { FATAL("Unable to register audio stream"); return false; } } } //6. prepare the response pFrom.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); //7. Done return pFrom.SendResponseMessage(); }
public bool HandleRTSPRequest(RtspProtocol from,Variant requestHeaders,string requestContent) { string method = requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD]; //1. we need a CSeq if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ] == null) { FATAL("Request doesn't have {0}:\n{1}", RTSP_HEADERS_CSEQ, requestHeaders); return false; } //2. Validate session id string wantedSessionId = from.SessionId; if (!string.IsNullOrEmpty(wantedSessionId)) { var requestSessionId = ""; if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION] == null) { FATAL("No session id"); return false; } requestSessionId = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION]; var parts = requestSessionId.Split(';'); if (parts.Length >= 1) { requestSessionId = parts[0]; } if (requestSessionId != wantedSessionId) { FATAL("Invalid session ID. Wanted: `{0}`; Got: `{1}`", (wantedSessionId), (requestSessionId)); return false; } } //4. Prepare a fresh new response. Add the sequence number from.ClearResponseMessage(); from.PushResponseHeader(RTSP_HEADERS_CSEQ, requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ]); //5. Do we have authentication? We will authenticate everything except "OPTIONS" if ( !string.IsNullOrEmpty(_usersFile) && NeedAuthentication(from,requestHeaders,requestContent)) { //6. Re-parse authentication file if necessary if (!ParseUsersFile()) { FATAL("Unable to parse authentication file"); return false; } //7. Get the real name to use it further in authentication process string realmName = GetAuthenticationRealm(from, requestHeaders, requestContent); //8. Do we have that realm? if (_realms[realmName]==null) { FATAL("Realm `{0}` not found", (realmName)); return false; } Variant realm = _realms[realmName]; //9. Is the user even trying to authenticate? if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION] == null) { return SendAuthenticationChallenge(from, realm); } else { //14. The client sent us some response. Validate it now //Did we ever sent him an authorization challange? if (from.CustomParameters["wwwAuthenticate"] ==null) { FATAL("Client tried to authenticate and the server didn't required that"); return false; } //15. Get the server challenge string wwwAuthenticate = from.CustomParameters["wwwAuthenticate"]; //16. Get the client response string authorization = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION]; //17. Try to authenticate if (!HTTPAuthHelper.ValidateAuthRequest(wwwAuthenticate, authorization, method, (string)requestHeaders[RTSP_FIRST_LINE,RTSP_URL], realm)) { WARN("Authorization failed: challenge: {0}; response: {1}", wwwAuthenticate, authorization); return SendAuthenticationChallenge(from, realm); } //18. Success. User authenticated //INFO("User authenticated: %s", (authorization)); } } switch (method) { case RTSP_METHOD_OPTIONS: from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); from.PushResponseHeader(RTSP_HEADERS_PUBLIC, "DESCRIBE, OPTIONS, PAUSE, PLAY, SETUP, TEARDOWN, ANNOUNCE, RECORD"); return from.SendResponseMessage(); case RTSP_METHOD_DESCRIBE: //1. get the stream name Uri uri = new Uri(requestHeaders[RTSP_FIRST_LINE,RTSP_URL]); string streamName = (uri.Segments.LastOrDefault(x=>!x.EndsWith("/"))??"")+uri.Query; if (streamName == "") { FATAL("Invalid stream name"); return false; } //2. Get the inbound stream capabilities IInNetStream pInStream = GetInboundStream(streamName); //3. Prepare the body of the response string outboundContent = ComputeSDP(from, streamName, "", "0.0.0.0"); if (outboundContent == "") { FATAL("Unable to compute SDP"); return false; } //4. Save the stream id for later usage from.CustomParameters["streamId"] = pInStream.UniqueId; //5. Mark this connection as outbound connection from.CustomParameters["isInbound"] = false; //6. prepare the response from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); from.PushResponseHeader(RTSP_HEADERS_CONTENT_TYPE, RTSP_HEADERS_ACCEPT_APPLICATIONSDP); from.PushResponseContent(outboundContent, false); //7. Done return from.SendResponseMessage(); case RTSP_METHOD_SETUP: if (from.CustomParameters["isInbound"] != VariantType.Boolean) { FATAL("Invalid state"); return false; } return @from.CustomParameters["isInbound"] ? HandleRTSPRequestSetupInbound(@from, requestHeaders, requestContent) : HandleRTSPRequestSetupOutbound(@from, requestHeaders, requestContent); case RTSP_METHOD_PLAY: return HandleRTSPRequestPlay(from, requestHeaders, requestContent); case RTSP_METHOD_TEARDOWN: from.EnqueueForDelete(); return true; case RTSP_METHOD_ANNOUNCE: return HandleRTSPRequestAnnounce(from, requestHeaders, requestContent); case RTSP_METHOD_RECORD: //1. Make sure we have everything and we are in the proper state if ((from.CustomParameters["isInbound"] != VariantType.Boolean) || ((bool)from.CustomParameters["isInbound"] != true)) { FATAL("Invalid state"); return false; } if (from.CustomParameters["pendingTracks"] != VariantType.Map) { FATAL("Invalid state"); return false; } //3. Get the inbound connectivity InboundConnectivity pConnectivity = from.InboundConnectivity; if (pConnectivity == null) { FATAL("Unable to get inbound connectivity"); return false; } if (!pConnectivity.Initialize()) { FATAL("Unable to initialize inbound connectivity"); return false; } //4. Send back the response from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); return from.SendResponseMessage(); case RTSP_METHOD_PAUSE: from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); return from.SendResponseMessage(); default: return false; } }