| 1 | /********** |
| 2 | This library is free software; you can redistribute it and/or modify it under |
| 3 | the terms of the GNU Lesser General Public License as published by the |
| 4 | Free Software Foundation; either version 3 of the License, or (at your |
| 5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| 6 | |
| 7 | This library is distributed in the hope that it will be useful, but WITHOUT |
| 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| 10 | more details. |
| 11 | |
| 12 | You should have received a copy of the GNU Lesser General Public License |
| 13 | along with this library; if not, write to the Free Software Foundation, Inc., |
| 14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 15 | **********/ |
| 16 | // "liveMedia" |
| 17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| 18 | // A RTSP server |
| 19 | // Implementation of functionality related to the "REGISTER" and "DEREGISTER" commands |
| 20 | |
| 21 | #include "RTSPServer.hh" |
| 22 | #include "RTSPCommon.hh" |
| 23 | #include "RTSPRegisterSender.hh" |
| 24 | #include "ProxyServerMediaSession.hh" |
| 25 | #include "GroupsockHelper.hh" |
| 26 | |
| 27 | ////////// Implementation of "RTSPServer::registerStream()": ////////// |
| 28 | |
| 29 | static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward |
| 30 | |
| 31 | // A class that represents the state of a "REGISTER" request in progress: |
| 32 | class RegisterRequestRecord: public RTSPRegisterSender { |
| 33 | public: |
| 34 | RegisterRequestRecord(RTSPServer& ourServer, unsigned requestId, |
| 35 | char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToRegister, |
| 36 | RTSPServer::responseHandlerForREGISTER* responseHandler, Authenticator* authenticator, |
| 37 | Boolean requestStreamingViaTCP, char const* proxyURLSuffix) |
| 38 | : RTSPRegisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToRegister, |
| 39 | rtspRegisterResponseHandler, authenticator, |
| 40 | requestStreamingViaTCP, proxyURLSuffix, True/*reuseConnection*/, |
| 41 | #ifdef DEBUG |
| 42 | 1/*verbosityLevel*/, |
| 43 | #else |
| 44 | 0/*verbosityLevel*/, |
| 45 | #endif |
| 46 | NULL), |
| 47 | fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) { |
| 48 | // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table: |
| 49 | ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this); |
| 50 | } |
| 51 | |
| 52 | virtual ~RegisterRequestRecord() { |
| 53 | // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go: |
| 54 | fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this); |
| 55 | } |
| 56 | |
| 57 | void handleResponse(int resultCode, char* resultString) { |
| 58 | if (resultCode == 0) { |
| 59 | // The "REGISTER" request succeeded, so use the still-open RTSP socket to await incoming commands from the remote endpoint: |
| 60 | int sock; |
| 61 | struct sockaddr_in remoteAddress; |
| 62 | |
| 63 | grabConnection(sock, remoteAddress); |
| 64 | if (sock >= 0) { |
| 65 | increaseSendBufferTo(envir(), sock, 50*1024); // in anticipation of streaming over it |
| 66 | (void)fOurServer.createNewClientConnection(sock, remoteAddress); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | if (fResponseHandler != NULL) { |
| 71 | // Call our (REGISTER-specific) response handler now: |
| 72 | (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString); |
| 73 | } else { |
| 74 | // We need to delete[] "resultString" before we leave: |
| 75 | delete[] resultString; |
| 76 | } |
| 77 | |
| 78 | // We're completely done with the REGISTER command now, so delete ourself now: |
| 79 | Medium::close(this); |
| 80 | } |
| 81 | |
| 82 | private: |
| 83 | RTSPServer& fOurServer; |
| 84 | unsigned fRequestId; |
| 85 | RTSPServer::responseHandlerForREGISTER* fResponseHandler; |
| 86 | }; |
| 87 | |
| 88 | static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) { |
| 89 | RegisterRequestRecord* registerRequestRecord = (RegisterRequestRecord*)rtspClient; |
| 90 | |
| 91 | registerRequestRecord->handleResponse(resultCode, resultString); |
| 92 | } |
| 93 | |
| 94 | unsigned RTSPServer::registerStream(ServerMediaSession* serverMediaSession, |
| 95 | char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, |
| 96 | responseHandlerForREGISTER* responseHandler, |
| 97 | char const* username, char const* password, |
| 98 | Boolean receiveOurStreamViaTCP, char const* proxyURLSuffix) { |
| 99 | // Create a new "RegisterRequestRecord" that will send the "REGISTER" command. |
| 100 | // (This object will automatically get deleted after we get a response to the "REGISTER" command, or if we're deleted.) |
| 101 | Authenticator* authenticator = NULL; |
| 102 | if (username != NULL) { |
| 103 | if (password == NULL) password = "" ; |
| 104 | authenticator = new Authenticator(username, password); |
| 105 | } |
| 106 | unsigned requestId = ++fRegisterOrDeregisterRequestCounter; |
| 107 | char const* url = rtspURL(serverMediaSession); |
| 108 | new RegisterRequestRecord(*this, requestId, |
| 109 | remoteClientNameOrAddress, remoteClientPortNum, url, |
| 110 | responseHandler, authenticator, |
| 111 | receiveOurStreamViaTCP, proxyURLSuffix); |
| 112 | |
| 113 | delete[] url; // we can do this here because it was copied to the "RegisterRequestRecord" |
| 114 | delete authenticator; // ditto |
| 115 | return requestId; |
| 116 | } |
| 117 | |
| 118 | ////////// Implementation of "RTSPServer::deregisterStream()": ////////// |
| 119 | |
| 120 | static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward |
| 121 | |
| 122 | // A class that represents the state of a "DEREGISTER" request in progress: |
| 123 | class DeregisterRequestRecord: public RTSPDeregisterSender { |
| 124 | public: |
| 125 | DeregisterRequestRecord(RTSPServer& ourServer, unsigned requestId, |
| 126 | char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToDeregister, |
| 127 | RTSPServer::responseHandlerForDEREGISTER* responseHandler, Authenticator* authenticator, |
| 128 | char const* proxyURLSuffix) |
| 129 | : RTSPDeregisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToDeregister, |
| 130 | rtspDeregisterResponseHandler, authenticator, proxyURLSuffix, |
| 131 | #ifdef DEBUG |
| 132 | 1/*verbosityLevel*/, |
| 133 | #else |
| 134 | 0/*verbosityLevel*/, |
| 135 | #endif |
| 136 | NULL), |
| 137 | fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) { |
| 138 | // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table: |
| 139 | ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this); |
| 140 | } |
| 141 | |
| 142 | virtual ~DeregisterRequestRecord() { |
| 143 | // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go: |
| 144 | fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this); |
| 145 | } |
| 146 | |
| 147 | void handleResponse(int resultCode, char* resultString) { |
| 148 | if (fResponseHandler != NULL) { |
| 149 | // Call our (DEREGISTER-specific) response handler now: |
| 150 | (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString); |
| 151 | } else { |
| 152 | // We need to delete[] "resultString" before we leave: |
| 153 | delete[] resultString; |
| 154 | } |
| 155 | |
| 156 | // We're completely done with the DEREGISTER command now, so delete ourself now: |
| 157 | Medium::close(this); |
| 158 | } |
| 159 | |
| 160 | private: |
| 161 | RTSPServer& fOurServer; |
| 162 | unsigned fRequestId; |
| 163 | RTSPServer::responseHandlerForDEREGISTER* fResponseHandler; |
| 164 | }; |
| 165 | |
| 166 | static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) { |
| 167 | DeregisterRequestRecord* deregisterRequestRecord = (DeregisterRequestRecord*)rtspClient; |
| 168 | |
| 169 | deregisterRequestRecord->handleResponse(resultCode, resultString); |
| 170 | } |
| 171 | |
| 172 | unsigned RTSPServer::deregisterStream(ServerMediaSession* serverMediaSession, |
| 173 | char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, |
| 174 | responseHandlerForDEREGISTER* responseHandler, |
| 175 | char const* username, char const* password, |
| 176 | char const* proxyURLSuffix) { |
| 177 | // Create a new "DeregisterRequestRecord" that will send the "DEREGISTER" command. |
| 178 | // (This object will automatically get deleted after we get a response to the "DEREGISTER" command, or if we're deleted.) |
| 179 | Authenticator* authenticator = NULL; |
| 180 | if (username != NULL) { |
| 181 | if (password == NULL) password = "" ; |
| 182 | authenticator = new Authenticator(username, password); |
| 183 | } |
| 184 | unsigned requestId = ++fRegisterOrDeregisterRequestCounter; |
| 185 | char const* url = rtspURL(serverMediaSession); |
| 186 | new DeregisterRequestRecord(*this, requestId, |
| 187 | remoteClientNameOrAddress, remoteClientPortNum, url, |
| 188 | responseHandler, authenticator, |
| 189 | proxyURLSuffix); |
| 190 | |
| 191 | delete[] url; // we can do this here because it was copied to the "DeregisterRequestRecord" |
| 192 | delete authenticator; // ditto |
| 193 | return requestId; |
| 194 | } |
| 195 | |
| 196 | Boolean RTSPServer::weImplementREGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/, |
| 197 | char const* /*proxyURLSuffix*/, char*& responseStr) { |
| 198 | // By default, servers do not implement our custom "REGISTER"/"DEREGISTER" commands: |
| 199 | responseStr = NULL; |
| 200 | return False; |
| 201 | } |
| 202 | |
| 203 | void RTSPServer::implementCmd_REGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/, |
| 204 | char const* /*url*/, char const* /*urlSuffix*/, int /*socketToRemoteServer*/, |
| 205 | Boolean /*deliverViaTCP*/, char const* /*proxyURLSuffix*/) { |
| 206 | // By default, this function is a 'noop' |
| 207 | } |
| 208 | |
| 209 | // Special mechanism for handling our custom "REGISTER" command: |
| 210 | |
| 211 | RTSPServer::RTSPClientConnection::ParamsForREGISTER |
| 212 | ::ParamsForREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| 213 | RTSPServer::RTSPClientConnection* ourConnection, char const* url, char const* urlSuffix, |
| 214 | Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) |
| 215 | : fCmd(strDup(cmd)), fOurConnection(ourConnection), fURL(strDup(url)), fURLSuffix(strDup(urlSuffix)), |
| 216 | fReuseConnection(reuseConnection), fDeliverViaTCP(deliverViaTCP), fProxyURLSuffix(strDup(proxyURLSuffix)) { |
| 217 | } |
| 218 | |
| 219 | RTSPServer::RTSPClientConnection::ParamsForREGISTER::~ParamsForREGISTER() { |
| 220 | delete[] (char*)fCmd; delete[] fURL; delete[] fURLSuffix; delete[] fProxyURLSuffix; |
| 221 | } |
| 222 | |
| 223 | #define DELAY_USECS_AFTER_REGISTER_RESPONSE 100000 /*100ms*/ |
| 224 | |
| 225 | void RTSPServer |
| 226 | ::RTSPClientConnection::handleCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| 227 | char const* url, char const* urlSuffix, char const* fullRequestStr, |
| 228 | Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) { |
| 229 | char* responseStr; |
| 230 | if (fOurRTSPServer.weImplementREGISTER(cmd, proxyURLSuffix, responseStr)) { |
| 231 | // The "REGISTER"/"DEREGISTER" command - if we implement it - may require access control: |
| 232 | if (!authenticationOK(cmd, urlSuffix, fullRequestStr)) return; |
| 233 | |
| 234 | // We implement the "REGISTER"/"DEREGISTER" command by first replying to it, then actually |
| 235 | // handling it (in a separate event-loop task, that will get called after the reply has |
| 236 | // been done). |
| 237 | // Hack: If we're going to reuse the command's connection for subsequent RTSP commands, then we |
| 238 | // delay the actual handling of the command slightly, to make it less likely that the first |
| 239 | // subsequent RTSP command (e.g., "DESCRIBE") will end up in the client's reponse buffer before |
| 240 | // the socket (at the far end) gets reused for RTSP command handling. |
| 241 | setRTSPResponse(responseStr == NULL ? "200 OK" : responseStr); |
| 242 | delete[] responseStr; |
| 243 | |
| 244 | ParamsForREGISTER* registerParams = new ParamsForREGISTER(cmd, this, url, urlSuffix, reuseConnection, deliverViaTCP, proxyURLSuffix); |
| 245 | envir().taskScheduler().scheduleDelayedTask(reuseConnection ? DELAY_USECS_AFTER_REGISTER_RESPONSE : 0, |
| 246 | (TaskFunc*)continueHandlingREGISTER, registerParams); |
| 247 | } else if (responseStr != NULL) { |
| 248 | setRTSPResponse(responseStr); |
| 249 | delete[] responseStr; |
| 250 | } else { |
| 251 | handleCmd_notSupported(); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | // A special version of "parseTransportHeader()", used just for parsing the "Transport:" header in an incoming "REGISTER" command: |
| 256 | void (char const* buf, |
| 257 | Boolean &reuseConnection, |
| 258 | Boolean& deliverViaTCP, |
| 259 | char*& proxyURLSuffix) { |
| 260 | // Initialize the result parameters to default values: |
| 261 | reuseConnection = False; |
| 262 | deliverViaTCP = False; |
| 263 | proxyURLSuffix = NULL; |
| 264 | |
| 265 | // First, find "Transport:" |
| 266 | while (1) { |
| 267 | if (*buf == '\0') return; // not found |
| 268 | if (*buf == '\r' && *(buf+1) == '\n' && *(buf+2) == '\r') return; // end of the headers => not found |
| 269 | if (_strncasecmp(buf, "Transport:" , 10) == 0) break; |
| 270 | ++buf; |
| 271 | } |
| 272 | |
| 273 | // Then, run through each of the fields, looking for ones we handle: |
| 274 | char const* fields = buf + 10; |
| 275 | while (*fields == ' ') ++fields; |
| 276 | char* field = strDupSize(fields); |
| 277 | while (sscanf(fields, "%[^;\r\n]" , field) == 1) { |
| 278 | if (strcmp(field, "reuse_connection" ) == 0) { |
| 279 | reuseConnection = True; |
| 280 | } else if (_strncasecmp(field, "preferred_delivery_protocol=udp" , 31) == 0) { |
| 281 | deliverViaTCP = False; |
| 282 | } else if (_strncasecmp(field, "preferred_delivery_protocol=interleaved" , 39) == 0) { |
| 283 | deliverViaTCP = True; |
| 284 | } else if (_strncasecmp(field, "proxy_url_suffix=" , 17) == 0) { |
| 285 | delete[] proxyURLSuffix; |
| 286 | proxyURLSuffix = strDup(field+17); |
| 287 | } |
| 288 | |
| 289 | fields += strlen(field); |
| 290 | while (*fields == ';' || *fields == ' ' || *fields == '\t') ++fields; // skip over separating ';' chars or whitespace |
| 291 | if (*fields == '\0' || *fields == '\r' || *fields == '\n') break; |
| 292 | } |
| 293 | delete[] field; |
| 294 | } |
| 295 | |
| 296 | void RTSPServer::RTSPClientConnection::continueHandlingREGISTER(ParamsForREGISTER* params) { |
| 297 | params->fOurConnection->continueHandlingREGISTER1(params); |
| 298 | } |
| 299 | |
| 300 | void RTSPServer::RTSPClientConnection::continueHandlingREGISTER1(ParamsForREGISTER* params) { |
| 301 | // Reuse our socket if requested: |
| 302 | int socketNumToBackEndServer = params->fReuseConnection ? fClientOutputSocket : -1; |
| 303 | |
| 304 | RTSPServer* ourServer = &fOurRTSPServer; // copy the pointer now, in case we "delete this" below |
| 305 | |
| 306 | if (socketNumToBackEndServer >= 0) { |
| 307 | // Because our socket will no longer be used by the server to handle incoming requests, we can now delete this |
| 308 | // "RTSPClientConnection" object. We do this now, in case the "implementCmd_REGISTER()" call below would also end up |
| 309 | // deleting this. |
| 310 | fClientInputSocket = fClientOutputSocket = -1; // so the socket doesn't get closed when we get deleted |
| 311 | delete this; |
| 312 | } |
| 313 | |
| 314 | ourServer->implementCmd_REGISTER(params->fCmd, |
| 315 | params->fURL, params->fURLSuffix, socketNumToBackEndServer, |
| 316 | params->fDeliverViaTCP, params->fProxyURLSuffix); |
| 317 | delete params; |
| 318 | } |
| 319 | |
| 320 | |
| 321 | ///////// RTSPServerWithREGISTERProxying implementation ///////// |
| 322 | |
| 323 | RTSPServerWithREGISTERProxying* RTSPServerWithREGISTERProxying |
| 324 | ::createNew(UsageEnvironment& env, Port ourPort, |
| 325 | UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER, |
| 326 | unsigned reclamationSeconds, |
| 327 | Boolean streamRTPOverTCP, int verbosityLevelForProxying, |
| 328 | char const* backEndUsername, char const* backEndPassword) { |
| 329 | int ourSocket = setUpOurSocket(env, ourPort); |
| 330 | if (ourSocket == -1) return NULL; |
| 331 | |
| 332 | return new RTSPServerWithREGISTERProxying(env, ourSocket, ourPort, |
| 333 | authDatabase, authDatabaseForREGISTER, |
| 334 | reclamationSeconds, |
| 335 | streamRTPOverTCP, verbosityLevelForProxying, |
| 336 | backEndUsername, backEndPassword); |
| 337 | } |
| 338 | |
| 339 | RTSPServerWithREGISTERProxying |
| 340 | ::RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort, |
| 341 | UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER, |
| 342 | unsigned reclamationSeconds, |
| 343 | Boolean streamRTPOverTCP, int verbosityLevelForProxying, |
| 344 | char const* backEndUsername, char const* backEndPassword) |
| 345 | : RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds), |
| 346 | fStreamRTPOverTCP(streamRTPOverTCP), fVerbosityLevelForProxying(verbosityLevelForProxying), |
| 347 | fRegisteredProxyCounter(0), fAllowedCommandNames(NULL), fAuthDBForREGISTER(authDatabaseForREGISTER), |
| 348 | fBackEndUsername(strDup(backEndUsername)), fBackEndPassword(strDup(backEndPassword)) { |
| 349 | } |
| 350 | |
| 351 | RTSPServerWithREGISTERProxying::~RTSPServerWithREGISTERProxying() { |
| 352 | delete[] fAllowedCommandNames; |
| 353 | delete[] fBackEndUsername; delete[] fBackEndPassword; |
| 354 | } |
| 355 | |
| 356 | char const* RTSPServerWithREGISTERProxying::allowedCommandNames() { |
| 357 | if (fAllowedCommandNames == NULL) { |
| 358 | char const* baseAllowedCommandNames = RTSPServer::allowedCommandNames(); |
| 359 | char const* newAllowedCommandName = ", REGISTER, DEREGISTER" ; |
| 360 | fAllowedCommandNames = new char[strlen(baseAllowedCommandNames) + strlen(newAllowedCommandName) + 1/* for '\0' */]; |
| 361 | sprintf(fAllowedCommandNames, "%s%s" , baseAllowedCommandNames, newAllowedCommandName); |
| 362 | } |
| 363 | return fAllowedCommandNames; |
| 364 | } |
| 365 | |
| 366 | Boolean RTSPServerWithREGISTERProxying |
| 367 | ::weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| 368 | char const* proxyURLSuffix, char*& responseStr) { |
| 369 | // First, check whether we have already proxied a stream as "proxyURLSuffix": |
| 370 | if (proxyURLSuffix != NULL) { |
| 371 | ServerMediaSession* sms = lookupServerMediaSession(proxyURLSuffix); |
| 372 | if ((strcmp(cmd, "REGISTER" ) == 0 && sms != NULL) || |
| 373 | (strcmp(cmd, "DEREGISTER" ) == 0 && sms == NULL)) { |
| 374 | responseStr = strDup("451 Invalid parameter" ); |
| 375 | return False; |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | // Otherwise, we will implement it: |
| 380 | responseStr = NULL; |
| 381 | return True; |
| 382 | } |
| 383 | |
| 384 | void RTSPServerWithREGISTERProxying |
| 385 | ::implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/, |
| 386 | char const* url, char const* /*urlSuffix*/, int socketToRemoteServer, |
| 387 | Boolean deliverViaTCP, char const* proxyURLSuffix) { |
| 388 | // Continue setting up proxying for the specified URL. |
| 389 | // By default: |
| 390 | // - We use "registeredProxyStream-N" as the (front-end) stream name (ignoring the back-end stream's 'urlSuffix'), |
| 391 | // unless "proxyURLSuffix" is non-NULL (in which case we use that) |
| 392 | // - There is no 'username' and 'password' for the back-end stream. (Thus, access-controlled back-end streams will fail.) |
| 393 | // - If "fStreamRTPOverTCP" is True, then we request delivery over TCP, regardless of the value of "deliverViaTCP". |
| 394 | // (Otherwise, if "fStreamRTPOverTCP" is False, we use the value of "deliverViaTCP" to decide this.) |
| 395 | // To change this default behavior, you will need to subclass "RTSPServerWithREGISTERProxying", and reimplement this function. |
| 396 | |
| 397 | char const* proxyStreamName; |
| 398 | char proxyStreamNameBuf[100]; |
| 399 | if (proxyURLSuffix == NULL) { |
| 400 | sprintf(proxyStreamNameBuf, "registeredProxyStream-%u" , ++fRegisteredProxyCounter); |
| 401 | proxyStreamName = proxyStreamNameBuf; |
| 402 | } else { |
| 403 | proxyStreamName = proxyURLSuffix; |
| 404 | } |
| 405 | |
| 406 | if (strcmp(cmd, "REGISTER" ) == 0) { |
| 407 | if (fStreamRTPOverTCP) deliverViaTCP = True; |
| 408 | portNumBits tunnelOverHTTPPortNum = deliverViaTCP ? (portNumBits)(~0) : 0; |
| 409 | // 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 |
| 410 | |
| 411 | ServerMediaSession* sms |
| 412 | = ProxyServerMediaSession::createNew(envir(), this, url, proxyStreamName, |
| 413 | fBackEndUsername, fBackEndPassword, |
| 414 | tunnelOverHTTPPortNum, fVerbosityLevelForProxying, socketToRemoteServer); |
| 415 | addServerMediaSession(sms); |
| 416 | |
| 417 | // (Regardless of the verbosity level) announce the fact that we're proxying this new stream, and the URL to use to access it: |
| 418 | char* proxyStreamURL = rtspURL(sms); |
| 419 | envir() << "Proxying the registered back-end stream \"" << url << "\".\n" ; |
| 420 | envir() << "\tPlay this stream using the URL: " << proxyStreamURL << "\n" ; |
| 421 | delete[] proxyStreamURL; |
| 422 | } else { // "DEREGISTER" |
| 423 | deleteServerMediaSession(lookupServerMediaSession(proxyStreamName)); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | UserAuthenticationDatabase* RTSPServerWithREGISTERProxying::getAuthenticationDatabaseForCommand(char const* cmdName) { |
| 428 | if (strcmp(cmdName, "REGISTER" ) == 0) return fAuthDBForREGISTER; |
| 429 | |
| 430 | return RTSPServer::getAuthenticationDatabaseForCommand(cmdName); |
| 431 | } |
| 432 | |