| /********** |
| This library is free software; you can redistribute it and/or modify it under |
| the terms of the GNU Lesser General Public License as published by the |
| Free Software Foundation; either version 3 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| This library is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this library; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| **********/ |
| // "liveMedia" |
| // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| // A RTSP server |
| // Implementation of functionality related to the "REGISTER" and "DEREGISTER" commands |
| |
| #include "RTSPServer.hh" |
| #include "RTSPCommon.hh" |
| #include "RTSPRegisterSender.hh" |
| #include "ProxyServerMediaSession.hh" |
| #include "GroupsockHelper.hh" |
| |
| ////////// Implementation of "RTSPServer::registerStream()": ////////// |
| |
| static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward |
| |
| // A class that represents the state of a "REGISTER" request in progress: |
| class RegisterRequestRecord: public RTSPRegisterSender { |
| public: |
| RegisterRequestRecord(RTSPServer& ourServer, unsigned requestId, |
| char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToRegister, |
| RTSPServer::responseHandlerForREGISTER* responseHandler, Authenticator* authenticator, |
| Boolean requestStreamingViaTCP, char const* proxyURLSuffix) |
| : RTSPRegisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToRegister, |
| rtspRegisterResponseHandler, authenticator, |
| requestStreamingViaTCP, proxyURLSuffix, True/*reuseConnection*/, |
| #ifdef DEBUG |
| 1/*verbosityLevel*/, |
| #else |
| 0/*verbosityLevel*/, |
| #endif |
| NULL), |
| fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) { |
| // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table: |
| ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this); |
| } |
| |
| virtual ~RegisterRequestRecord() { |
| // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go: |
| fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this); |
| } |
| |
| void handleResponse(int resultCode, char* resultString) { |
| if (resultCode == 0) { |
| // The "REGISTER" request succeeded, so use the still-open RTSP socket to await incoming commands from the remote endpoint: |
| int sock; |
| struct sockaddr_in remoteAddress; |
| |
| grabConnection(sock, remoteAddress); |
| if (sock >= 0) { |
| increaseSendBufferTo(envir(), sock, 50*1024); // in anticipation of streaming over it |
| (void)fOurServer.createNewClientConnection(sock, remoteAddress); |
| } |
| } |
| |
| if (fResponseHandler != NULL) { |
| // Call our (REGISTER-specific) response handler now: |
| (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString); |
| } else { |
| // We need to delete[] "resultString" before we leave: |
| delete[] resultString; |
| } |
| |
| // We're completely done with the REGISTER command now, so delete ourself now: |
| Medium::close(this); |
| } |
| |
| private: |
| RTSPServer& fOurServer; |
| unsigned fRequestId; |
| RTSPServer::responseHandlerForREGISTER* fResponseHandler; |
| }; |
| |
| static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) { |
| RegisterRequestRecord* registerRequestRecord = (RegisterRequestRecord*)rtspClient; |
| |
| registerRequestRecord->handleResponse(resultCode, resultString); |
| } |
| |
| unsigned RTSPServer::registerStream(ServerMediaSession* serverMediaSession, |
| char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, |
| responseHandlerForREGISTER* responseHandler, |
| char const* username, char const* password, |
| Boolean receiveOurStreamViaTCP, char const* proxyURLSuffix) { |
| // Create a new "RegisterRequestRecord" that will send the "REGISTER" command. |
| // (This object will automatically get deleted after we get a response to the "REGISTER" command, or if we're deleted.) |
| Authenticator* authenticator = NULL; |
| if (username != NULL) { |
| if (password == NULL) password = ""; |
| authenticator = new Authenticator(username, password); |
| } |
| unsigned requestId = ++fRegisterOrDeregisterRequestCounter; |
| char const* url = rtspURL(serverMediaSession); |
| new RegisterRequestRecord(*this, requestId, |
| remoteClientNameOrAddress, remoteClientPortNum, url, |
| responseHandler, authenticator, |
| receiveOurStreamViaTCP, proxyURLSuffix); |
| |
| delete[] (char*)url; // we can do this here because it was copied to the "RegisterRequestRecord" |
| delete authenticator; // ditto |
| return requestId; |
| } |
| |
| ////////// Implementation of "RTSPServer::deregisterStream()": ////////// |
| |
| static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward |
| |
| // A class that represents the state of a "DEREGISTER" request in progress: |
| class DeregisterRequestRecord: public RTSPDeregisterSender { |
| public: |
| DeregisterRequestRecord(RTSPServer& ourServer, unsigned requestId, |
| char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToDeregister, |
| RTSPServer::responseHandlerForDEREGISTER* responseHandler, Authenticator* authenticator, |
| char const* proxyURLSuffix) |
| : RTSPDeregisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToDeregister, |
| rtspDeregisterResponseHandler, authenticator, proxyURLSuffix, |
| #ifdef DEBUG |
| 1/*verbosityLevel*/, |
| #else |
| 0/*verbosityLevel*/, |
| #endif |
| NULL), |
| fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) { |
| // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table: |
| ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this); |
| } |
| |
| virtual ~DeregisterRequestRecord() { |
| // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go: |
| fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this); |
| } |
| |
| void handleResponse(int resultCode, char* resultString) { |
| if (fResponseHandler != NULL) { |
| // Call our (DEREGISTER-specific) response handler now: |
| (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString); |
| } else { |
| // We need to delete[] "resultString" before we leave: |
| delete[] resultString; |
| } |
| |
| // We're completely done with the DEREGISTER command now, so delete ourself now: |
| Medium::close(this); |
| } |
| |
| private: |
| RTSPServer& fOurServer; |
| unsigned fRequestId; |
| RTSPServer::responseHandlerForDEREGISTER* fResponseHandler; |
| }; |
| |
| static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) { |
| DeregisterRequestRecord* deregisterRequestRecord = (DeregisterRequestRecord*)rtspClient; |
| |
| deregisterRequestRecord->handleResponse(resultCode, resultString); |
| } |
| |
| unsigned RTSPServer::deregisterStream(ServerMediaSession* serverMediaSession, |
| char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, |
| responseHandlerForDEREGISTER* responseHandler, |
| char const* username, char const* password, |
| char const* proxyURLSuffix) { |
| // Create a new "DeregisterRequestRecord" that will send the "DEREGISTER" command. |
| // (This object will automatically get deleted after we get a response to the "DEREGISTER" command, or if we're deleted.) |
| Authenticator* authenticator = NULL; |
| if (username != NULL) { |
| if (password == NULL) password = ""; |
| authenticator = new Authenticator(username, password); |
| } |
| unsigned requestId = ++fRegisterOrDeregisterRequestCounter; |
| char const* url = rtspURL(serverMediaSession); |
| new DeregisterRequestRecord(*this, requestId, |
| remoteClientNameOrAddress, remoteClientPortNum, url, |
| responseHandler, authenticator, |
| proxyURLSuffix); |
| |
| delete[] (char*)url; // we can do this here because it was copied to the "DeregisterRequestRecord" |
| delete authenticator; // ditto |
| return requestId; |
| } |
| |
| Boolean RTSPServer::weImplementREGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/, |
| char const* /*proxyURLSuffix*/, char*& responseStr) { |
| // By default, servers do not implement our custom "REGISTER"/"DEREGISTER" commands: |
| responseStr = NULL; |
| return False; |
| } |
| |
| void RTSPServer::implementCmd_REGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/, |
| char const* /*url*/, char const* /*urlSuffix*/, int /*socketToRemoteServer*/, |
| Boolean /*deliverViaTCP*/, char const* /*proxyURLSuffix*/) { |
| // By default, this function is a 'noop' |
| } |
| |
| // Special mechanism for handling our custom "REGISTER" command: |
| |
| RTSPServer::RTSPClientConnection::ParamsForREGISTER |
| ::ParamsForREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| RTSPServer::RTSPClientConnection* ourConnection, char const* url, char const* urlSuffix, |
| Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) |
| : fCmd(strDup(cmd)), fOurConnection(ourConnection), fURL(strDup(url)), fURLSuffix(strDup(urlSuffix)), |
| fReuseConnection(reuseConnection), fDeliverViaTCP(deliverViaTCP), fProxyURLSuffix(strDup(proxyURLSuffix)) { |
| } |
| |
| RTSPServer::RTSPClientConnection::ParamsForREGISTER::~ParamsForREGISTER() { |
| delete[] (char*)fCmd; delete[] fURL; delete[] fURLSuffix; delete[] fProxyURLSuffix; |
| } |
| |
| #define DELAY_USECS_AFTER_REGISTER_RESPONSE 100000 /*100ms*/ |
| |
| void RTSPServer |
| ::RTSPClientConnection::handleCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| char const* url, char const* urlSuffix, char const* fullRequestStr, |
| Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) { |
| char* responseStr; |
| if (fOurRTSPServer.weImplementREGISTER(cmd, proxyURLSuffix, responseStr)) { |
| // The "REGISTER"/"DEREGISTER" command - if we implement it - may require access control: |
| if (!authenticationOK(cmd, urlSuffix, fullRequestStr)) return; |
| |
| // We implement the "REGISTER"/"DEREGISTER" command by first replying to it, then actually |
| // handling it (in a separate event-loop task, that will get called after the reply has |
| // been done). |
| // Hack: If we're going to reuse the command's connection for subsequent RTSP commands, then we |
| // delay the actual handling of the command slightly, to make it less likely that the first |
| // subsequent RTSP command (e.g., "DESCRIBE") will end up in the client's reponse buffer before |
| // the socket (at the far end) gets reused for RTSP command handling. |
| setRTSPResponse(responseStr == NULL ? "200 OK" : responseStr); |
| delete[] responseStr; |
| |
| ParamsForREGISTER* registerParams = new ParamsForREGISTER(cmd, this, url, urlSuffix, reuseConnection, deliverViaTCP, proxyURLSuffix); |
| envir().taskScheduler().scheduleDelayedTask(reuseConnection ? DELAY_USECS_AFTER_REGISTER_RESPONSE : 0, |
| (TaskFunc*)continueHandlingREGISTER, registerParams); |
| } else if (responseStr != NULL) { |
| setRTSPResponse(responseStr); |
| delete[] responseStr; |
| } else { |
| handleCmd_notSupported(); |
| } |
| } |
| |
| // A special version of "parseTransportHeader()", used just for parsing the "Transport:" header in an incoming "REGISTER" command: |
| void parseTransportHeaderForREGISTER(char const* buf, |
| Boolean &reuseConnection, |
| Boolean& deliverViaTCP, |
| char*& proxyURLSuffix) { |
| // Initialize the result parameters to default values: |
| reuseConnection = False; |
| deliverViaTCP = False; |
| proxyURLSuffix = NULL; |
| |
| // First, find "Transport:" |
| while (1) { |
| if (*buf == '\0') return; // not found |
| if (*buf == '\r' && *(buf+1) == '\n' && *(buf+2) == '\r') return; // end of the headers => not found |
| if (_strncasecmp(buf, "Transport:", 10) == 0) break; |
| ++buf; |
| } |
| |
| // Then, run through each of the fields, looking for ones we handle: |
| char const* fields = buf + 10; |
| while (*fields == ' ') ++fields; |
| char* field = strDupSize(fields); |
| while (sscanf(fields, "%[^;\r\n]", field) == 1) { |
| if (strcmp(field, "reuse_connection") == 0) { |
| reuseConnection = True; |
| } else if (_strncasecmp(field, "preferred_delivery_protocol=udp", 31) == 0) { |
| deliverViaTCP = False; |
| } else if (_strncasecmp(field, "preferred_delivery_protocol=interleaved", 39) == 0) { |
| deliverViaTCP = True; |
| } else if (_strncasecmp(field, "proxy_url_suffix=", 17) == 0) { |
| delete[] proxyURLSuffix; |
| proxyURLSuffix = strDup(field+17); |
| } |
| |
| fields += strlen(field); |
| while (*fields == ';' || *fields == ' ' || *fields == '\t') ++fields; // skip over separating ';' chars or whitespace |
| if (*fields == '\0' || *fields == '\r' || *fields == '\n') break; |
| } |
| delete[] field; |
| } |
| |
| void RTSPServer::RTSPClientConnection::continueHandlingREGISTER(ParamsForREGISTER* params) { |
| params->fOurConnection->continueHandlingREGISTER1(params); |
| } |
| |
| void RTSPServer::RTSPClientConnection::continueHandlingREGISTER1(ParamsForREGISTER* params) { |
| // Reuse our socket if requested: |
| int socketNumToBackEndServer = params->fReuseConnection ? fClientOutputSocket : -1; |
| |
| RTSPServer* ourServer = &fOurRTSPServer; // copy the pointer now, in case we "delete this" below |
| |
| if (socketNumToBackEndServer >= 0) { |
| // Because our socket will no longer be used by the server to handle incoming requests, we can now delete this |
| // "RTSPClientConnection" object. We do this now, in case the "implementCmd_REGISTER()" call below would also end up |
| // deleting this. |
| fClientInputSocket = fClientOutputSocket = -1; // so the socket doesn't get closed when we get deleted |
| delete this; |
| } |
| |
| ourServer->implementCmd_REGISTER(params->fCmd, |
| params->fURL, params->fURLSuffix, socketNumToBackEndServer, |
| params->fDeliverViaTCP, params->fProxyURLSuffix); |
| delete params; |
| } |
| |
| |
| ///////// RTSPServerWithREGISTERProxying implementation ///////// |
| |
| RTSPServerWithREGISTERProxying* RTSPServerWithREGISTERProxying |
| ::createNew(UsageEnvironment& env, Port ourPort, |
| UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER, |
| unsigned reclamationSeconds, |
| Boolean streamRTPOverTCP, int verbosityLevelForProxying, |
| char const* backEndUsername, char const* backEndPassword) { |
| int ourSocket = setUpOurSocket(env, ourPort); |
| if (ourSocket == -1) return NULL; |
| |
| return new RTSPServerWithREGISTERProxying(env, ourSocket, ourPort, |
| authDatabase, authDatabaseForREGISTER, |
| reclamationSeconds, |
| streamRTPOverTCP, verbosityLevelForProxying, |
| backEndUsername, backEndPassword); |
| } |
| |
| RTSPServerWithREGISTERProxying |
| ::RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort, |
| UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER, |
| unsigned reclamationSeconds, |
| Boolean streamRTPOverTCP, int verbosityLevelForProxying, |
| char const* backEndUsername, char const* backEndPassword) |
| : RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds), |
| fStreamRTPOverTCP(streamRTPOverTCP), fVerbosityLevelForProxying(verbosityLevelForProxying), |
| fRegisteredProxyCounter(0), fAllowedCommandNames(NULL), fAuthDBForREGISTER(authDatabaseForREGISTER), |
| fBackEndUsername(strDup(backEndUsername)), fBackEndPassword(strDup(backEndPassword)) { |
| } |
| |
| RTSPServerWithREGISTERProxying::~RTSPServerWithREGISTERProxying() { |
| delete[] fAllowedCommandNames; |
| delete[] fBackEndUsername; delete[] fBackEndPassword; |
| } |
| |
| char const* RTSPServerWithREGISTERProxying::allowedCommandNames() { |
| if (fAllowedCommandNames == NULL) { |
| char const* baseAllowedCommandNames = RTSPServer::allowedCommandNames(); |
| char const* newAllowedCommandName = ", REGISTER, DEREGISTER"; |
| fAllowedCommandNames = new char[strlen(baseAllowedCommandNames) + strlen(newAllowedCommandName) + 1/* for '\0' */]; |
| sprintf(fAllowedCommandNames, "%s%s", baseAllowedCommandNames, newAllowedCommandName); |
| } |
| return fAllowedCommandNames; |
| } |
| |
| Boolean RTSPServerWithREGISTERProxying |
| ::weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| char const* proxyURLSuffix, char*& responseStr) { |
| // First, check whether we have already proxied a stream as "proxyURLSuffix": |
| if (proxyURLSuffix != NULL) { |
| ServerMediaSession* sms = lookupServerMediaSession(proxyURLSuffix); |
| if ((strcmp(cmd, "REGISTER") == 0 && sms != NULL) || |
| (strcmp(cmd, "DEREGISTER") == 0 && sms == NULL)) { |
| responseStr = strDup("451 Invalid parameter"); |
| return False; |
| } |
| } |
| |
| // Otherwise, we will implement it: |
| responseStr = NULL; |
| return True; |
| } |
| |
| void RTSPServerWithREGISTERProxying |
| ::implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| char const* url, char const* /*urlSuffix*/, int socketToRemoteServer, |
| Boolean deliverViaTCP, char const* proxyURLSuffix) { |
| // Continue setting up proxying for the specified URL. |
| // By default: |
| // - We use "registeredProxyStream-N" as the (front-end) stream name (ignoring the back-end stream's 'urlSuffix'), |
| // unless "proxyURLSuffix" is non-NULL (in which case we use that) |
| // - There is no 'username' and 'password' for the back-end stream. (Thus, access-controlled back-end streams will fail.) |
| // - If "fStreamRTPOverTCP" is True, then we request delivery over TCP, regardless of the value of "deliverViaTCP". |
| // (Otherwise, if "fStreamRTPOverTCP" is False, we use the value of "deliverViaTCP" to decide this.) |
| // To change this default behavior, you will need to subclass "RTSPServerWithREGISTERProxying", and reimplement this function. |
| |
| char const* proxyStreamName; |
| char proxyStreamNameBuf[100]; |
| if (proxyURLSuffix == NULL) { |
| sprintf(proxyStreamNameBuf, "registeredProxyStream-%u", ++fRegisteredProxyCounter); |
| proxyStreamName = proxyStreamNameBuf; |
| } else { |
| proxyStreamName = proxyURLSuffix; |
| } |
| |
| if (strcmp(cmd, "REGISTER") == 0) { |
| if (fStreamRTPOverTCP) deliverViaTCP = True; |
| portNumBits tunnelOverHTTPPortNum = deliverViaTCP ? (portNumBits)(~0) : 0; |
| // We don't support streaming from the back-end via RTSP/RTP/RTCP-over-HTTP; only via RTP/RTCP-over-TCP or RTP/RTCP-over-UDP |
| |
| ServerMediaSession* sms |
| = ProxyServerMediaSession::createNew(envir(), this, url, proxyStreamName, |
| fBackEndUsername, fBackEndPassword, |
| tunnelOverHTTPPortNum, fVerbosityLevelForProxying, socketToRemoteServer); |
| addServerMediaSession(sms); |
| |
| // (Regardless of the verbosity level) announce the fact that we're proxying this new stream, and the URL to use to access it: |
| char* proxyStreamURL = rtspURL(sms); |
| envir() << "Proxying the registered back-end stream \"" << url << "\".\n"; |
| envir() << "\tPlay this stream using the URL: " << proxyStreamURL << "\n"; |
| delete[] proxyStreamURL; |
| } else { // "DEREGISTER" |
| deleteServerMediaSession(lookupServerMediaSession(proxyStreamName)); |
| } |
| } |
| |
| UserAuthenticationDatabase* RTSPServerWithREGISTERProxying::getAuthenticationDatabaseForCommand(char const* cmdName) { |
| if (strcmp(cmdName, "REGISTER") == 0) return fAuthDBForREGISTER; |
| |
| return RTSPServer::getAuthenticationDatabaseForCommand(cmdName); |
| } |