blob: 77fd8527fe15af2ff6a95405310b127f7e51e6c9 [file] [log] [blame]
/**********
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);
}