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 HandleRTSPRequestSetupOutbound(RtspProtocol @from, Variant requestHeaders, string requestContent) { //1. Minimal sanitize if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT] == null) { FATAL("RTSP %s request doesn't have %s header line", RTSP_METHOD_SETUP, RTSP_HEADERS_TRANSPORT); return false; } //2. get the transport header line string raw = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT]; Variant transport = Variant.Get(); if (!Rtsp.SDP.ParseTransportLine(raw, transport)) { FATAL("Unable to parse transport line {0}", (raw)); return false; } bool forceTcp = false; if (transport["client_port"]!=null && (transport["rtp/avp/udp"]!=null || transport["rtp/avp"]!=null)) { forceTcp = false; } else if (transport["interleaved"]!=null && transport["rtp/avp/tcp"]!=null) { forceTcp = true; } else { FATAL("Invalid transport line: {0}", (transport.ToString())); return false; } from.CustomParameters["forceTcp"] = forceTcp; //3. Get the outbound connectivity OutboundConnectivity pOutboundConnectivity = GetOutboundConnectivity(from, forceTcp); if (pOutboundConnectivity == null) { FATAL("Unable to get the outbound connectivity"); return false; } //5. Find out if this is audio or video bool isAudioTrack; string rawUrl = requestHeaders[RTSP_FIRST_LINE,RTSP_URL]; string audioTrackId = "trackID=" + from.CustomParameters["audioTrackId"]; string videoTrackId = "trackID=" + from.CustomParameters["videoTrackId"]; /*FINEST("rawUrl: %s; audioTrackId: %s; videoTrackId: %s; fa: %d; fv: %d", (rawUrl), (audioTrackId), (videoTrackId), rawUrl.find(audioTrackId) != string::npos, rawUrl.find(videoTrackId) != string::npos );*/ if (rawUrl.Contains(audioTrackId)) { isAudioTrack = true; } else if (rawUrl.Contains(videoTrackId)) { isAudioTrack = false; } else { FATAL("Invalid track. Wanted: {0} or {1}; Got: {2}", (from.CustomParameters["audioTrackId"]), (from.CustomParameters["videoTrackId"]), (requestHeaders[RTSP_FIRST_LINE,RTSP_URL])); return false; } from.CustomParameters["isAudioTrack"] = isAudioTrack; if (isAudioTrack) { if (forceTcp) { from.CustomParameters["audioDataChannelNumber"] = transport["interleaved","data"]; from.CustomParameters["audioRtcpChannelNumber"] = transport["interleaved","rtcp"]; from.CustomParameters["audioTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL]; pOutboundConnectivity.HasAudio = true; } else { from.CustomParameters["audioDataPortNumber"] = transport["client_port","data"]; from.CustomParameters["audioRtcpPortNumber"] = transport["client_port","rtcp"]; from.CustomParameters["audioTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL]; pOutboundConnectivity.HasAudio = true; } } else { if (forceTcp) { from.CustomParameters["videoDataChannelNumber"] = transport["interleaved","data"]; from.CustomParameters["videoRtcpChannelNumber"] = transport["interleaved","rtcp"]; from.CustomParameters["videoTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL]; pOutboundConnectivity.HasVideo = true; } else { from.CustomParameters["videoDataPortNumber"] = transport["client_port","data"]; from.CustomParameters["videoRtcpPortNumber"] = transport["client_port","rtcp"]; from.CustomParameters["videoTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL]; pOutboundConnectivity.HasVideo = true; } } //10. Create a session from.GenerateSessionId(); //11 Compose the response from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK"); @from.PushResponseHeader(RTSP_HEADERS_TRANSPORT, forceTcp ? "RTP/AVP/TCP;unicast;interleaved="+ transport["interleaved","all"] : $"RTP/AVP/UDP;unicast;source={((TCPCarrier) @from.IOHandler).NearIP};client_port={(string)(transport["client_port","all"])};server_port={(isAudioTrack ? (pOutboundConnectivity.AudioPorts) : (pOutboundConnectivity.VideoPorts))};ssrc={(isAudioTrack ? pOutboundConnectivity.AudioSSRC : pOutboundConnectivity.VideoSSRC)}"); //12. Done return from.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; } }