liveMedia/RTSPServer.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2012 Live Networks, Inc.  All rights reserved.
00018 // A RTSP server
00019 // Implementation
00020 
00021 #include "RTSPServer.hh"
00022 #include "RTSPCommon.hh"
00023 #include "Base64.hh"
00024 #include <GroupsockHelper.hh>
00025 
00026 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00027 #else
00028 #include <signal.h>
00029 #define USE_SIGNALS 1
00030 #endif
00031 
00033 
00034 RTSPServer*
00035 RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
00036                       UserAuthenticationDatabase* authDatabase,
00037                       unsigned reclamationTestSeconds) {
00038   int ourSocket = setUpOurSocket(env, ourPort);
00039   if (ourSocket == -1) return NULL;
00040 
00041   return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
00042 }
00043 
00044 Boolean RTSPServer::lookupByName(UsageEnvironment& env,
00045                                  char const* name,
00046                                  RTSPServer*& resultServer) {
00047   resultServer = NULL; // unless we succeed
00048 
00049   Medium* medium;
00050   if (!Medium::lookupByName(env, name, medium)) return False;
00051 
00052   if (!medium->isRTSPServer()) {
00053     env.setResultMsg(name, " is not a RTSP server");
00054     return False;
00055   }
00056 
00057   resultServer = (RTSPServer*)medium;
00058   return True;
00059 }
00060 
00061 void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
00062   if (serverMediaSession == NULL) return;
00063 
00064   char const* sessionName = serverMediaSession->streamName();
00065   if (sessionName == NULL) sessionName = "";
00066   removeServerMediaSession(sessionName); // in case an existing "ServerMediaSession" with this name already exists
00067 
00068   fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);
00069 }
00070 
00071 ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
00072   return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
00073 }
00074 
00075 void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
00076   if (serverMediaSession == NULL) return;
00077 
00078   fServerMediaSessions->Remove(serverMediaSession->streamName());
00079   if (serverMediaSession->referenceCount() == 0) {
00080     Medium::close(serverMediaSession);
00081   } else {
00082     serverMediaSession->deleteWhenUnreferenced() = True;
00083   }
00084 }
00085 
00086 void RTSPServer::removeServerMediaSession(char const* streamName) {
00087   removeServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
00088 }
00089 
00090 char* RTSPServer
00091 ::rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket) const {
00092   char* urlPrefix = rtspURLPrefix(clientSocket);
00093   char const* sessionName = serverMediaSession->streamName();
00094 
00095   char* resultURL = new char[strlen(urlPrefix) + strlen(sessionName) + 1];
00096   sprintf(resultURL, "%s%s", urlPrefix, sessionName);
00097 
00098   delete[] urlPrefix;
00099   return resultURL;
00100 }
00101 
00102 char* RTSPServer::rtspURLPrefix(int clientSocket) const {
00103   struct sockaddr_in ourAddress;
00104   if (clientSocket < 0) {
00105     // Use our default IP address in the URL:
00106     ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0
00107       ? ReceivingInterfaceAddr
00108       : ourIPAddress(envir()); // hack
00109   } else {
00110     SOCKLEN_T namelen = sizeof ourAddress;
00111     getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen);
00112   }
00113 
00114   char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"
00115 
00116   portNumBits portNumHostOrder = ntohs(fRTSPServerPort.num());
00117   if (portNumHostOrder == 554 /* the default port number */) {
00118     sprintf(urlBuffer, "rtsp://%s/", AddressString(ourAddress).val());
00119   } else {
00120     sprintf(urlBuffer, "rtsp://%s:%hu/",
00121             AddressString(ourAddress).val(), portNumHostOrder);
00122   }
00123 
00124   return strDup(urlBuffer);
00125 }
00126 
00127 UserAuthenticationDatabase* RTSPServer::setAuthenticationDatabase(UserAuthenticationDatabase* newDB) {
00128   UserAuthenticationDatabase* oldDB = fAuthDB;
00129   fAuthDB = newDB;
00130 
00131   return oldDB;
00132 }
00133 
00134 Boolean RTSPServer::setUpTunnelingOverHTTP(Port httpPort) {
00135   fHTTPServerSocket = setUpOurSocket(envir(), httpPort);
00136   if (fHTTPServerSocket >= 0) {
00137     fHTTPServerPort = httpPort;
00138     envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocket,
00139                                                          (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerHTTP, this);
00140     return True;
00141   }
00142 
00143   return False;
00144 }
00145 
00146 portNumBits RTSPServer::httpServerPortNum() const {
00147   return ntohs(fHTTPServerPort.num());
00148 }
00149 
00150 #define LISTEN_BACKLOG_SIZE 20
00151 
00152 int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
00153   int ourSocket = -1;
00154 
00155   do {
00156     // The following statement is enabled by default.
00157     // Don't disable it (by defining ALLOW_RTSP_SERVER_PORT_REUSE) unless you know what you're doing.
00158 #ifndef ALLOW_RTSP_SERVER_PORT_REUSE
00159     NoReuse dummy(env); // Don't use this socket if there's already a local server using it
00160 #endif
00161 
00162     ourSocket = setupStreamSocket(env, ourPort);
00163     if (ourSocket < 0) break;
00164 
00165     // Make sure we have a big send buffer:
00166     if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
00167 
00168     // Allow multiple simultaneous connections:
00169     if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
00170       env.setResultErrMsg("listen() failed: ");
00171       break;
00172     }
00173 
00174     if (ourPort.num() == 0) {
00175       // bind() will have chosen a port for us; return it also:
00176       if (!getSourcePort(env, ourSocket, ourPort)) break;
00177     }
00178 
00179     return ourSocket;
00180   } while (0);
00181 
00182   if (ourSocket != -1) ::closeSocket(ourSocket);
00183   return -1;
00184 }
00185 
00186 Boolean RTSPServer
00187 ::specialClientAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/, char const* /*urlSuffix*/) {
00188   // default implementation
00189   return True;
00190 }
00191 
00192 RTSPServer::RTSPServer(UsageEnvironment& env,
00193                        int ourSocket, Port ourPort,
00194                        UserAuthenticationDatabase* authDatabase,
00195                        unsigned reclamationTestSeconds)
00196   : Medium(env),
00197     fRTSPServerSocket(ourSocket), fRTSPServerPort(ourPort),
00198     fHTTPServerSocket(-1), fHTTPServerPort(0), fClientSessionsForHTTPTunneling(NULL),
00199     fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
00200     fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)) {
00201 #ifdef USE_SIGNALS
00202   // Ignore the SIGPIPE signal, so that clients on the same host that are killed
00203   // don't also kill us:
00204 #ifdef SO_NOSIGPIPE
00205   int set_option = 1;
00206   setsockopt(ourSocket, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
00207 #else
00208   signal(SIGPIPE, SIG_IGN);
00209 #endif
00210 #endif
00211 
00212   // Arrange to handle connections from others:
00213   env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
00214                                                    (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
00215 }
00216 
00217 RTSPServer::~RTSPServer() {
00218   // Turn off background read handling:
00219   envir().taskScheduler().turnOffBackgroundReadHandling(fRTSPServerSocket);
00220   ::closeSocket(fRTSPServerSocket);
00221 
00222   envir().taskScheduler().turnOffBackgroundReadHandling(fHTTPServerSocket);
00223   ::closeSocket(fHTTPServerSocket);
00224 
00225   delete fClientSessionsForHTTPTunneling;
00226 
00227   // Remove all server media sessions (they'll get deleted when they're finished):
00228   while (1) {
00229     ServerMediaSession* serverMediaSession
00230       = (ServerMediaSession*)fServerMediaSessions->RemoveNext();
00231     if (serverMediaSession == NULL) break;
00232     removeServerMediaSession(serverMediaSession);
00233   }
00234 
00235   // Finally, delete the session table itself:
00236   delete fServerMediaSessions;
00237 }
00238 
00239 Boolean RTSPServer::isRTSPServer() const {
00240   return True;
00241 }
00242 
00243 void RTSPServer::incomingConnectionHandlerRTSP(void* instance, int /*mask*/) {
00244   RTSPServer* server = (RTSPServer*)instance;
00245   server->incomingConnectionHandlerRTSP1();
00246 }
00247 void RTSPServer::incomingConnectionHandlerRTSP1() {
00248   incomingConnectionHandler(fRTSPServerSocket);
00249 }
00250 
00251 void RTSPServer::incomingConnectionHandlerHTTP(void* instance, int /*mask*/) {
00252   RTSPServer* server = (RTSPServer*)instance;
00253   server->incomingConnectionHandlerHTTP1();
00254 }
00255 void RTSPServer::incomingConnectionHandlerHTTP1() {
00256   incomingConnectionHandler(fHTTPServerSocket);
00257 }
00258 
00259 void RTSPServer::incomingConnectionHandler(int serverSocket) {
00260   struct sockaddr_in clientAddr;
00261   SOCKLEN_T clientAddrLen = sizeof clientAddr;
00262   int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
00263   if (clientSocket < 0) {
00264     int err = envir().getErrno();
00265     if (err != EWOULDBLOCK) {
00266         envir().setResultErrMsg("accept() failed: ");
00267     }
00268     return;
00269   }
00270   makeSocketNonBlocking(clientSocket);
00271   increaseSendBufferTo(envir(), clientSocket, 50*1024);
00272 
00273 #ifdef DEBUG
00274   envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
00275 #endif
00276 
00277   // Create a new object for this RTSP session.
00278   // (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number).  We don't bother checking for
00279   //  a collision; the probability of two concurrent sessions getting the same session id is very low.)
00280   // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
00281   unsigned sessionId;
00282   do { sessionId = (unsigned)our_random32(); } while (sessionId == 0);
00283   (void)createNewClientSession(sessionId, clientSocket, clientAddr);
00284 }
00285 
00286 
00288 
00289 RTSPServer::RTSPClientSession
00290 ::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr)
00291   : fOurServer(ourServer), fOurSessionId(sessionId),
00292     fOurServerMediaSession(NULL),
00293     fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
00294     fSessionCookie(NULL), fLivenessCheckTask(NULL),
00295     fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
00296     fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL), fRecursionCount(0) {
00297   // Arrange to handle incoming requests:
00298   resetRequestBuffer();
00299   envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
00300      (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00301   noteLiveness();
00302 }
00303 
00304 RTSPServer::RTSPClientSession::~RTSPClientSession() {
00305   closeSockets();
00306 
00307   if (fSessionCookie != NULL) {
00308     // We were being used for RTSP-over-HTTP tunneling.  Remove ourselves from the 'session cookie' hash table before we go:
00309     fOurServer.fClientSessionsForHTTPTunneling->Remove(fSessionCookie);
00310     delete[] fSessionCookie;
00311   }
00312 
00313   reclaimStreamStates();
00314 
00315   if (fOurServerMediaSession != NULL) {
00316     fOurServerMediaSession->decrementReferenceCount();
00317     if (fOurServerMediaSession->referenceCount() == 0
00318         && fOurServerMediaSession->deleteWhenUnreferenced()) {
00319       fOurServer.removeServerMediaSession(fOurServerMediaSession);
00320       fOurServerMediaSession = NULL;
00321     }
00322   }
00323 }
00324 
00325 void RTSPServer::RTSPClientSession::closeSockets() {
00326   // Turn off any liveness checking:
00327   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
00328 
00329   // Turn off background read handling:
00330   envir().taskScheduler().turnOffBackgroundReadHandling(fClientInputSocket);
00331 
00332   if (fClientOutputSocket != fClientInputSocket) ::closeSocket(fClientOutputSocket);
00333   ::closeSocket(fClientInputSocket);
00334 
00335   fClientInputSocket = fClientOutputSocket = -1;
00336 }
00337 
00338 void RTSPServer::RTSPClientSession::reclaimStreamStates() {
00339   for (unsigned i = 0; i < fNumStreamStates; ++i) {
00340     if (fStreamStates[i].subsession != NULL) {
00341       fStreamStates[i].subsession->deleteStream(fOurSessionId,
00342                                                 fStreamStates[i].streamToken);
00343     }
00344   }
00345   delete[] fStreamStates; fStreamStates = NULL;
00346   fNumStreamStates = 0;
00347 }
00348 
00349 void RTSPServer::RTSPClientSession::resetRequestBuffer() {
00350   fRequestBytesAlreadySeen = 0;
00351   fRequestBufferBytesLeft = sizeof fRequestBuffer;
00352   fLastCRLF = &fRequestBuffer[-3]; // hack: Ensures that we don't think we have end-of-msg if the data starts with <CR><LF>
00353   fBase64RemainderCount = 0;
00354 }
00355 
00356 void RTSPServer::RTSPClientSession::incomingRequestHandler(void* instance, int /*mask*/) {
00357   RTSPClientSession* session = (RTSPClientSession*)instance;
00358   session->incomingRequestHandler1();
00359 }
00360 
00361 void RTSPServer::RTSPClientSession::incomingRequestHandler1() {
00362   struct sockaddr_in dummy; // 'from' address, meaningless in this case
00363 
00364   int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
00365   handleRequestBytes(bytesRead);
00366 }
00367 
00368 void RTSPServer::RTSPClientSession::handleAlternativeRequestByte(void* instance, u_int8_t requestByte) {
00369   RTSPClientSession* session = (RTSPClientSession*)instance;
00370   session->handleAlternativeRequestByte1(requestByte);
00371 }
00372 
00373 void RTSPServer::RTSPClientSession::handleAlternativeRequestByte1(u_int8_t requestByte) {
00374   // Add this character to our buffer; then try to handle the data that we have buffered so far:
00375   if (fRequestBufferBytesLeft == 0 || fRequestBytesAlreadySeen >= RTSP_BUFFER_SIZE) return;
00376   fRequestBuffer[fRequestBytesAlreadySeen] = requestByte;
00377   handleRequestBytes(1);
00378 }
00379 
00380 void RTSPServer::RTSPClientSession::handleRequestBytes(int newBytesRead) {
00381   int numBytesRemaining = 0;
00382   ++fRecursionCount;
00383 
00384   do {
00385     noteLiveness();
00386     
00387     if (newBytesRead <= 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
00388       // Either the client socket has died, or the request was too big for us.
00389       // Terminate this connection:
00390 #ifdef DEBUG
00391       fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
00392 #endif
00393       fSessionIsActive = False;
00394       break;
00395     }
00396     
00397     Boolean endOfMsg = False;
00398     unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
00399 #ifdef DEBUG
00400     ptr[newBytesRead] = '\0';
00401     fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() %s %d new bytes:%s\n",
00402             this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
00403 #endif
00404     
00405     if (fClientOutputSocket != fClientInputSocket) {
00406       // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
00407       // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes):
00408       unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
00409       unsigned newBase64RemainderCount = numBytesToDecode%4;
00410       numBytesToDecode -= newBase64RemainderCount;
00411       if (numBytesToDecode > 0) {
00412         ptr[newBytesRead] = '\0';
00413         unsigned decodedSize;
00414         unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount), decodedSize);
00415 #ifdef DEBUG
00416         fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
00417         for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
00418         fprintf(stderr, "\n");
00419 #endif
00420         
00421         // Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
00422         unsigned char* to = ptr-fBase64RemainderCount;
00423         for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
00424         
00425         // Then copy any remaining (undecoded) bytes to the end:
00426         for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
00427         
00428         newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
00429         delete[] decodedBytes;
00430       }
00431       fBase64RemainderCount = newBase64RemainderCount;
00432       if (fBase64RemainderCount > 0) break; // because we know that we have more input bytes still to receive
00433     }
00434     
00435     // Look for the end of the message: <CR><LF><CR><LF>
00436     unsigned char *tmpPtr = fLastCRLF + 2;
00437     if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
00438     while (tmpPtr < &ptr[newBytesRead-1]) {
00439       if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
00440         if (tmpPtr - fLastCRLF == 2) { // This is it:
00441           endOfMsg = True;
00442           break;
00443         }
00444         fLastCRLF = tmpPtr;
00445       }
00446       ++tmpPtr;
00447     }
00448     
00449     fRequestBufferBytesLeft -= newBytesRead;
00450     fRequestBytesAlreadySeen += newBytesRead;
00451     
00452     if (!endOfMsg) break; // subsequent reads will be needed to complete the request
00453     
00454     // Parse the request string into command name and 'CSeq', then handle the command:
00455     fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
00456     char cmdName[RTSP_PARAM_STRING_MAX];
00457     char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00458     char urlSuffix[RTSP_PARAM_STRING_MAX];
00459     char cseq[RTSP_PARAM_STRING_MAX];
00460     unsigned contentLength;
00461     *fLastCRLF = '\0'; // temporarily, for parsing
00462     Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
00463                                                     cmdName, sizeof cmdName,
00464                                                     urlPreSuffix, sizeof urlPreSuffix,
00465                                                     urlSuffix, sizeof urlSuffix,
00466                                                     cseq, sizeof cseq,
00467                                                     contentLength);
00468     *fLastCRLF = '\r';
00469     if (parseSucceeded) {
00470 #ifdef DEBUG
00471       fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
00472 #endif
00473       // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
00474       if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us 
00475       
00476       if (strcmp(cmdName, "OPTIONS") == 0) {
00477         handleCmd_OPTIONS(cseq);
00478       } else if (strcmp(cmdName, "DESCRIBE") == 0) {
00479         handleCmd_DESCRIBE(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00480       } else if (strcmp(cmdName, "SETUP") == 0) {
00481         handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00482       } else if (strcmp(cmdName, "TEARDOWN") == 0
00483                  || strcmp(cmdName, "PLAY") == 0
00484                  || strcmp(cmdName, "PAUSE") == 0
00485                  || strcmp(cmdName, "GET_PARAMETER") == 0
00486                  || strcmp(cmdName, "SET_PARAMETER") == 0) {
00487         handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer);
00488       } else {
00489         handleCmd_notSupported(cseq);
00490       }
00491     } else {
00492 #ifdef DEBUG
00493       fprintf(stderr, "parseRTSPRequestString() failed\n");
00494 #endif
00495       // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
00496       char sessionCookie[RTSP_PARAM_STRING_MAX];
00497       char acceptStr[RTSP_PARAM_STRING_MAX];
00498       *fLastCRLF = '\0'; // temporarily, for parsing
00499       parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
00500                                               urlSuffix, sizeof urlPreSuffix,
00501                                               sessionCookie, sizeof sessionCookie,
00502                                               acceptStr, sizeof acceptStr);
00503       *fLastCRLF = '\r';
00504       if (parseSucceeded) {
00505 #ifdef DEBUG
00506         fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
00507 #endif
00508         // Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
00509         Boolean isValidHTTPCmd = True;
00510         if (sessionCookie[0] == '\0') {
00511           // There was no "x-sessionCookie:" header.  If there was an "Accept: application/x-rtsp-tunnelled" header,
00512           // then this is a bad tunneling request.  Otherwise, assume that it's an attempt to access the stream via HTTP.
00513           if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
00514             isValidHTTPCmd = False;
00515           } else {
00516             handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
00517           }
00518         } else if (strcmp(cmdName, "GET") == 0) {
00519           handleHTTPCmd_TunnelingGET(sessionCookie);
00520         } else if (strcmp(cmdName, "POST") == 0) {
00521           // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
00522           // Check for this, and handle it if it exists:
00523           unsigned char const* extraData = fLastCRLF+4;
00524           unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
00525           if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
00526             // We don't respond to the "POST" command, and we go away:
00527             fSessionIsActive = False;
00528             break;
00529           }
00530         } else {
00531           isValidHTTPCmd = False;
00532         }
00533         if (!isValidHTTPCmd) {
00534           handleHTTPCmd_notSupported();
00535         }
00536       } else {
00537 #ifdef DEBUG
00538         fprintf(stderr, "parseHTTPRequestString() failed!\n");
00539 #endif
00540         handleCmd_bad(cseq);
00541       }
00542     }
00543     
00544 #ifdef DEBUG
00545     fprintf(stderr, "sending response: %s", fResponseBuffer);
00546 #endif
00547     send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00548     
00549     if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
00550       // The client has asked for streaming to commence now, rather than after a
00551       // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
00552       handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
00553                               (char const*)fRequestBuffer);
00554     }
00555     
00556     // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
00557     // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
00558     unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
00559     numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
00560     resetRequestBuffer(); // to prepare for any subsequent request
00561 
00562     if (numBytesRemaining > 0) {
00563       memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
00564       newBytesRead = numBytesRemaining;
00565     }
00566   } while (numBytesRemaining > 0);
00567 
00568   --fRecursionCount;
00569   if (!fSessionIsActive) {
00570     if (fRecursionCount > 0) closeSockets(); else delete this;
00571     // Note: The "fRecursionCount" test is for a pathological situation where we got called recursively while handling a command.
00572     // In such a case we don't want to actually delete ourself until we leave the outermost call.
00573   }
00574 }
00575 
00576 // Handler routines for specific RTSP commands:
00577 
00578 static char const* allowedCommandNames
00579 = "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER";
00580 
00581 void RTSPServer::RTSPClientSession::handleCmd_bad(char const* /*cseq*/) {
00582   // Don't do anything with "cseq", because it might be nonsense
00583   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00584            "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
00585            dateHeader(), allowedCommandNames);
00586 }
00587 
00588 void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) {
00589   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00590            "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
00591            cseq, dateHeader(), allowedCommandNames);
00592 }
00593 
00594 void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) {
00595   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00596            "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
00597            cseq, dateHeader());
00598   fSessionIsActive = False; // triggers deletion of ourself after responding
00599 }
00600 
00601 void RTSPServer::RTSPClientSession::handleCmd_unsupportedTransport(char const* cseq) {
00602   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00603            "RTSP/1.0 461 Unsupported Transport\r\nCSeq: %s\r\n%s\r\n",
00604            cseq, dateHeader());
00605   fSessionIsActive = False; // triggers deletion of ourself after responding
00606 }
00607 
00608 void RTSPServer::RTSPClientSession::handleCmd_OPTIONS(char const* cseq) {
00609   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00610            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
00611            cseq, dateHeader(), allowedCommandNames);
00612 }
00613 
00614 void RTSPServer::RTSPClientSession
00615 ::handleCmd_DESCRIBE(char const* cseq,
00616                      char const* urlPreSuffix, char const* urlSuffix,
00617                      char const* fullRequestStr) {
00618   char* sdpDescription = NULL;
00619   char* rtspURL = NULL;
00620   do {
00621     char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
00622     if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix) {
00623       handleCmd_bad(cseq);
00624       break;
00625     }
00626     urlTotalSuffix[0] = '\0';
00627     if (urlPreSuffix[0] != '\0') {
00628       strcat(urlTotalSuffix, urlPreSuffix);
00629       strcat(urlTotalSuffix, "/");
00630     }
00631     strcat(urlTotalSuffix, urlSuffix);
00632       
00633     if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr)) break;
00634     
00635     // We should really check that the request contains an "Accept:" #####
00636     // for "application/sdp", because that's what we're sending back #####
00637     
00638     // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
00639     ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
00640     if (session == NULL) {
00641       handleCmd_notFound(cseq);
00642       break;
00643     }
00644     
00645     // Then, assemble a SDP description for this session:
00646     sdpDescription = session->generateSDPDescription();
00647     if (sdpDescription == NULL) {
00648       // This usually means that a file name that was specified for a
00649       // "ServerMediaSubsession" does not exist.
00650       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00651                "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
00652                "CSeq: %s\r\n"
00653                "%s\r\n",
00654                cseq,
00655                dateHeader());
00656       break;
00657     }
00658     unsigned sdpDescriptionSize = strlen(sdpDescription);
00659     
00660     // Also, generate our RTSP URL, for the "Content-Base:" header
00661     // (which is necessary to ensure that the correct URL gets used in
00662     // subsequent "SETUP" requests).
00663     rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
00664     
00665     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00666              "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
00667              "%s"
00668              "Content-Base: %s/\r\n"
00669              "Content-Type: application/sdp\r\n"
00670              "Content-Length: %d\r\n\r\n"
00671              "%s",
00672              cseq,
00673              dateHeader(),
00674              rtspURL,
00675              sdpDescriptionSize,
00676              sdpDescription);
00677   } while (0);
00678 
00679   delete[] sdpDescription;
00680   delete[] rtspURL;
00681 }
00682 
00683 typedef enum StreamingMode {
00684   RTP_UDP,
00685   RTP_TCP,
00686   RAW_UDP
00687 } StreamingMode;
00688 
00689 static void parseTransportHeader(char const* buf,
00690                                  StreamingMode& streamingMode,
00691                                  char*& streamingModeString,
00692                                  char*& destinationAddressStr,
00693                                  u_int8_t& destinationTTL,
00694                                  portNumBits& clientRTPPortNum, // if UDP
00695                                  portNumBits& clientRTCPPortNum, // if UDP
00696                                  unsigned char& rtpChannelId, // if TCP
00697                                  unsigned char& rtcpChannelId // if TCP
00698                                  ) {
00699   // Initialize the result parameters to default values:
00700   streamingMode = RTP_UDP;
00701   streamingModeString = NULL;
00702   destinationAddressStr = NULL;
00703   destinationTTL = 255;
00704   clientRTPPortNum = 0;
00705   clientRTCPPortNum = 1;
00706   rtpChannelId = rtcpChannelId = 0xFF;
00707 
00708   portNumBits p1, p2;
00709   unsigned ttl, rtpCid, rtcpCid;
00710 
00711   // First, find "Transport:"
00712   while (1) {
00713     if (*buf == '\0') return; // not found
00714     if (*buf == '\r' && *(buf+1) == '\n' && *(buf+2) == '\r') return; // end of the headers => not found
00715     if (_strncasecmp(buf, "Transport: ", 11) == 0) break;
00716     ++buf;
00717   }
00718 
00719   // Then, run through each of the fields, looking for ones we handle:
00720   char const* fields = buf + 11;
00721   char* field = strDupSize(fields);
00722   while (sscanf(fields, "%[^;\r\n]", field) == 1) {
00723     if (strcmp(field, "RTP/AVP/TCP") == 0) {
00724       streamingMode = RTP_TCP;
00725     } else if (strcmp(field, "RAW/RAW/UDP") == 0 ||
00726                strcmp(field, "MP2T/H2221/UDP") == 0) {
00727       streamingMode = RAW_UDP;
00728       streamingModeString = strDup(field);
00729     } else if (_strncasecmp(field, "destination=", 12) == 0) {
00730       delete[] destinationAddressStr;
00731       destinationAddressStr = strDup(field+12);
00732     } else if (sscanf(field, "ttl%u", &ttl) == 1) {
00733       destinationTTL = (u_int8_t)ttl;
00734     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {
00735         clientRTPPortNum = p1;
00736         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p2; // ignore the second port number if the client asked for raw UDP
00737     } else if (sscanf(field, "client_port=%hu", &p1) == 1) {
00738         clientRTPPortNum = p1;
00739         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;
00740     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
00741       rtpChannelId = (unsigned char)rtpCid;
00742       rtcpChannelId = (unsigned char)rtcpCid;
00743     }
00744 
00745     fields += strlen(field);
00746     while (*fields == ';') ++fields; // skip over separating ';' chars
00747     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
00748   }
00749   delete[] field;
00750 }
00751 
00752 static Boolean parsePlayNowHeader(char const* buf) {
00753   // Find "x-playNow:" header, if present
00754   while (1) {
00755     if (*buf == '\0') return False; // not found
00756     if (_strncasecmp(buf, "x-playNow:", 10) == 0) break;
00757     ++buf;
00758   }
00759 
00760   return True;
00761 }
00762 
00763 void RTSPServer::RTSPClientSession
00764 ::handleCmd_SETUP(char const* cseq,
00765                   char const* urlPreSuffix, char const* urlSuffix,
00766                   char const* fullRequestStr) {
00767   // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.
00768   // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),
00769   // in the special case where we have only a single track.  I.e., in this case, we also handle:
00770   //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or
00771   //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.
00772   char const* streamName = urlPreSuffix; // in the normal case
00773   char const* trackId = urlSuffix; // in the normal case
00774   char* concatenatedStreamName = NULL; // in the normal case
00775 
00776   do {
00777     // First, make sure the specified stream name exists:
00778     ServerMediaSession* prevSMS = fOurServerMediaSession;
00779     fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
00780     if (fOurServerMediaSession == NULL) {
00781       // Check for the special case (noted above), before we give up:
00782       if (urlPreSuffix[0] == '\0') {
00783         streamName = urlSuffix;
00784       } else {
00785         concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'
00786         sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);
00787         streamName = concatenatedStreamName;
00788       }
00789       trackId = NULL;
00790 
00791       // Check again:
00792       fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
00793     }
00794     if (fOurServerMediaSession == NULL) {
00795       handleCmd_notFound(cseq);
00796       break;
00797     }
00798 
00799     if (fOurServerMediaSession != prevSMS) {
00800       // We're accessing the "ServerMediaSession" for the first time.
00801       fOurServerMediaSession->incrementReferenceCount();
00802     }
00803 
00804     if (fStreamStates == NULL) {
00805       // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
00806       ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
00807       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)
00808 
00809       fStreamStates = new struct streamState[fNumStreamStates];
00810 
00811       iter.reset();
00812       ServerMediaSubsession* subsession;
00813       for (unsigned i = 0; i < fNumStreamStates; ++i) {
00814         subsession = iter.next();
00815         fStreamStates[i].subsession = subsession;
00816         fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
00817       }
00818     }
00819 
00820     // Look up information for the specified subsession (track):
00821     ServerMediaSubsession* subsession = NULL;
00822     unsigned streamNum;
00823     if (trackId != NULL && trackId[0] != '\0') { // normal case
00824       for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
00825         subsession = fStreamStates[streamNum].subsession;
00826         if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
00827       }
00828       if (streamNum >= fNumStreamStates) {
00829         // The specified track id doesn't exist, so this request fails:
00830         handleCmd_notFound(cseq);
00831         break;
00832       }
00833     } else {
00834       // Weird case: there was no track id in the URL.
00835       // This works only if we have only one subsession:
00836       if (fNumStreamStates != 1) {
00837         handleCmd_bad(cseq);
00838         break;
00839       }
00840       streamNum = 0;
00841       subsession = fStreamStates[streamNum].subsession;
00842     }
00843     // ASSERT: subsession != NULL
00844 
00845     // Look for a "Transport:" header in the request string, to extract client parameters:
00846     StreamingMode streamingMode;
00847     char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
00848     char* clientsDestinationAddressStr;
00849     u_int8_t clientsDestinationTTL;
00850     portNumBits clientRTPPortNum, clientRTCPPortNum;
00851     unsigned char rtpChannelId, rtcpChannelId;
00852     parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
00853                          clientsDestinationAddressStr, clientsDestinationTTL,
00854                          clientRTPPortNum, clientRTCPPortNum,
00855                          rtpChannelId, rtcpChannelId);
00856     if (streamingMode == RTP_TCP && rtpChannelId == 0xFF ||
00857         streamingMode != RTP_TCP && fClientOutputSocket != fClientInputSocket) {
00858       // An anomolous situation, caused by a buggy client.  Either:
00859       //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or
00860       //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).
00861       // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:
00862       streamingMode = RTP_TCP;
00863       rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
00864     }
00865     fTCPStreamIdCount += 2;
00866 
00867     Port clientRTPPort(clientRTPPortNum);
00868     Port clientRTCPPort(clientRTCPPortNum);
00869 
00870     // Next, check whether a "Range:" header is present in the request.
00871     // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
00872     double rangeStart = 0.0, rangeEnd = 0.0;
00873     fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) || parsePlayNowHeader(fullRequestStr);
00874 
00875     // Then, get server parameters from the 'subsession':
00876     int tcpSocketNum = streamingMode == RTP_TCP ? fClientOutputSocket : -1;
00877     netAddressBits destinationAddress = 0;
00878     u_int8_t destinationTTL = 255;
00879 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
00880     if (clientsDestinationAddressStr != NULL) {
00881       // Use the client-provided "destination" address.
00882       // Note: This potentially allows the server to be used in denial-of-service
00883       // attacks, so don't enable this code unless you're sure that clients are
00884       // trusted.
00885       destinationAddress = our_inet_addr(clientsDestinationAddressStr);
00886     }
00887     // Also use the client-provided TTL.
00888     destinationTTL = clientsDestinationTTL;
00889 #endif
00890     delete[] clientsDestinationAddressStr;
00891     Port serverRTPPort(0);
00892     Port serverRTCPPort(0);
00893 
00894     // Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):
00895     struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
00896     getsockname(fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);
00897     netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;
00898     netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;
00899     // NOTE: The following might not work properly, so we ifdef it out for now:
00900 #ifdef HACK_FOR_MULTIHOMED_SERVERS
00901     ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;
00902 #endif
00903 
00904     subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
00905                                     clientRTPPort, clientRTCPPort,
00906                                     tcpSocketNum, rtpChannelId, rtcpChannelId,
00907                                     destinationAddress, destinationTTL, fIsMulticast,
00908                                     serverRTPPort, serverRTCPPort,
00909                                     fStreamStates[streamNum].streamToken);
00910     SendingInterfaceAddr = origSendingInterfaceAddr;
00911     ReceivingInterfaceAddr = origReceivingInterfaceAddr;
00912     
00913     AddressString destAddrStr(destinationAddress);
00914     AddressString sourceAddrStr(sourceAddr);
00915     if (fIsMulticast) {
00916       switch (streamingMode) {
00917         case RTP_UDP:
00918           snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00919                    "RTSP/1.0 200 OK\r\n"
00920                    "CSeq: %s\r\n"
00921                    "%s"
00922                    "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
00923                    "Session: %08X\r\n\r\n",
00924                    cseq,
00925                    dateHeader(),
00926                    destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
00927                    fOurSessionId);
00928           break;
00929         case RTP_TCP:
00930           // multicast streams can't be sent via TCP
00931           handleCmd_unsupportedTransport(cseq);
00932           break;
00933         case RAW_UDP:
00934           snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00935                    "RTSP/1.0 200 OK\r\n"
00936                    "CSeq: %s\r\n"
00937                    "%s"
00938                    "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
00939                    "Session: %08X\r\n\r\n",
00940                    cseq,
00941                    dateHeader(),
00942                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), destinationTTL,
00943                    fOurSessionId);
00944           break;
00945       }
00946     } else {
00947       switch (streamingMode) {
00948         case RTP_UDP: {
00949           snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00950                    "RTSP/1.0 200 OK\r\n"
00951                    "CSeq: %s\r\n"
00952                    "%s"
00953                    "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
00954                    "Session: %08X\r\n\r\n",
00955                    cseq,
00956                    dateHeader(),
00957                    destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
00958                    fOurSessionId);
00959           break;
00960         }
00961         case RTP_TCP: {
00962           snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00963                    "RTSP/1.0 200 OK\r\n"
00964                    "CSeq: %s\r\n"
00965                    "%s"
00966                    "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
00967                    "Session: %08X\r\n\r\n",
00968                    cseq,
00969                    dateHeader(),
00970                    destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
00971                    fOurSessionId);
00972           break;
00973         }
00974         case RAW_UDP: {
00975           snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00976                    "RTSP/1.0 200 OK\r\n"
00977                    "CSeq: %s\r\n"
00978                    "%s"
00979                    "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
00980                    "Session: %08X\r\n\r\n",
00981                    cseq,
00982                    dateHeader(),
00983                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
00984                    fOurSessionId);
00985           break;
00986         }
00987       }
00988     }
00989     delete[] streamingModeString;
00990   } while (0);
00991 
00992   delete[] concatenatedStreamName;
00993 }
00994 
00995 void RTSPServer::RTSPClientSession
00996 ::handleCmd_withinSession(char const* cmdName,
00997                           char const* urlPreSuffix, char const* urlSuffix,
00998                           char const* cseq, char const* fullRequestStr) {
00999   // This will either be:
01000   // - an operation on the entire server, if "urlPreSuffix" is "", and "urlSuffix" is "*" (i.e., the special "*" URL), or
01001   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
01002   //   name and "urlSuffix" is the subsession (track) name, or
01003   // - an aggregated operation, if "urlSuffix" is the session (stream) name,
01004   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,
01005   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.
01006   // Begin by figuring out which of these it is:
01007   ServerMediaSubsession* subsession;
01008   if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {
01009     // An operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:
01010     if (strcmp(cmdName, "GET_PARAMETER") == 0) {
01011       handleCmd_GET_PARAMETER(NULL, cseq, fullRequestStr);
01012     } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
01013       handleCmd_SET_PARAMETER(NULL, cseq, fullRequestStr);
01014     } else {
01015       handleCmd_notSupported(cseq);
01016     }
01017     return;
01018   } else if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
01019     handleCmd_notSupported(cseq);
01020     return;
01021   } else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
01022     // Non-aggregated operation.
01023     // Look up the media subsession whose track id is "urlSuffix":
01024     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01025     while ((subsession = iter.next()) != NULL) {
01026       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
01027     }
01028     if (subsession == NULL) { // no such track!
01029       handleCmd_notFound(cseq);
01030       return;
01031     }
01032   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
01033              (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {
01034     // Aggregated operation
01035     subsession = NULL;
01036   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {
01037     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:
01038     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);
01039     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&
01040         fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&
01041         strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {
01042       subsession = NULL;
01043     } else {
01044       handleCmd_notFound(cseq);
01045       return;
01046     }
01047   } else { // the request doesn't match a known stream and/or track at all!
01048     handleCmd_notFound(cseq);
01049     return;
01050   }
01051 
01052   if (strcmp(cmdName, "TEARDOWN") == 0) {
01053     handleCmd_TEARDOWN(subsession, cseq);
01054   } else if (strcmp(cmdName, "PLAY") == 0) {
01055     handleCmd_PLAY(subsession, cseq, fullRequestStr);
01056   } else if (strcmp(cmdName, "PAUSE") == 0) {
01057     handleCmd_PAUSE(subsession, cseq);
01058   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
01059     handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr);
01060   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
01061     handleCmd_SET_PARAMETER(subsession, cseq, fullRequestStr);
01062   }
01063 }
01064 
01065 void RTSPServer::RTSPClientSession
01066 ::handleCmd_TEARDOWN(ServerMediaSubsession* /*subsession*/, char const* cseq) {
01067   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01068            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%s\r\n",
01069            cseq, dateHeader());
01070   fSessionIsActive = False; // triggers deletion of ourself after responding
01071 }
01072 
01073 static Boolean parseScaleHeader(char const* buf, float& scale) {
01074   // Initialize the result parameter to a default value:
01075   scale = 1.0;
01076 
01077   // First, find "Scale:"
01078   while (1) {
01079     if (*buf == '\0') return False; // not found
01080     if (_strncasecmp(buf, "Scale: ", 7) == 0) break;
01081     ++buf;
01082   }
01083 
01084   // Then, run through each of the fields, looking for ones we handle:
01085   char const* fields = buf + 7;
01086   while (*fields == ' ') ++fields;
01087   float sc;
01088   if (sscanf(fields, "%f", &sc) == 1) {
01089     scale = sc;
01090   } else {
01091     return False; // The header is malformed
01092   }
01093 
01094   return True;
01095 }
01096 
01097 void RTSPServer::RTSPClientSession
01098   ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,
01099                    char const* fullRequestStr) {
01100   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientInputSocket);
01101   unsigned rtspURLSize = strlen(rtspURL);
01102 
01103   // Parse the client's "Scale:" header, if any:
01104   float scale;
01105   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
01106 
01107   // Try to set the stream's scale factor to this value:
01108   if (subsession == NULL /*aggregate op*/) {
01109     fOurServerMediaSession->testScaleFactor(scale);
01110   } else {
01111     subsession->testScaleFactor(scale);
01112   }
01113 
01114   char buf[100];
01115   char* scaleHeader;
01116   if (!sawScaleHeader) {
01117     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
01118   } else {
01119     sprintf(buf, "Scale: %f\r\n", scale);
01120   }
01121   scaleHeader = strDup(buf);
01122 
01123   // Parse the client's "Range:" header, if any:
01124   double rangeStart = 0.0, rangeEnd = 0.0;
01125   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd);
01126 
01127   // Use this information, plus the stream's duration (if known), to create
01128   // our own "Range:" header, for the response:
01129   float duration = subsession == NULL /*aggregate op*/
01130     ? fOurServerMediaSession->duration() : subsession->duration();
01131   if (duration < 0.0) {
01132     // We're an aggregate PLAY, but the subsessions have different durations.
01133     // Use the largest of these durations in our header
01134     duration = -duration;
01135   }
01136 
01137   // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values
01138   // before we send back our own "Range:" header in our response:
01139   if (rangeStart < 0.0) rangeStart = 0.0;
01140   else if (rangeStart > duration) rangeStart = duration;
01141   if (rangeEnd < 0.0) rangeEnd = 0.0;
01142   else if (rangeEnd > duration) rangeEnd = duration;
01143   if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0) ||
01144       (scale < 0.0 && rangeStart < rangeEnd)) {
01145     // "rangeStart" and "rangeEnd" were the wrong way around; swap them:
01146     double tmp = rangeStart;
01147     rangeStart = rangeEnd;
01148     rangeEnd = tmp;
01149   }
01150 
01151   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
01152   char const* rtpInfoFmt =
01153     "%s" // "RTP-Info:", plus any preceding rtpInfo items
01154     "%s" // comma separator, if needed
01155     "url=%s/%s"
01156     ";seq=%d"
01157     ";rtptime=%u"
01158     ;
01159   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
01160   char* rtpInfo = strDup("RTP-Info: ");
01161   unsigned i, numRTPInfoItems = 0;
01162 
01163   // Do any required seeking/scaling on each subsession, before starting streaming.
01164   // (However, we don't do this if the "PLAY" request was for just a single subsession of a multiple-subsession stream;
01165   //  for such streams, seeking/scaling can be done only with an aggregate "PLAY".)
01166   for (i = 0; i < fNumStreamStates; ++i) {
01167     if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {
01168       if (sawScaleHeader) {
01169         fStreamStates[i].subsession->setStreamScale(fOurSessionId,
01170                                                     fStreamStates[i].streamToken,
01171                                                     scale);
01172       }
01173       if (sawRangeHeader) {
01174         double streamDuration = 0.0; // by default; means: stream until the end of the media
01175         if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places
01176           // We want the stream to end early.  Set the duration we want:
01177           streamDuration = rangeEnd - rangeStart;
01178           if (streamDuration < 0.0) streamDuration = -streamDuration; // should happen only if scale < 0.0
01179         }
01180         u_int64_t numBytes;
01181         fStreamStates[i].subsession->seekStream(fOurSessionId,
01182                                                 fStreamStates[i].streamToken,
01183                                                 rangeStart, streamDuration, numBytes);
01184       }
01185     }
01186   }
01187 
01188   // Create the "Range:" header that we'll send back in our response.
01189   // (Note that we do this after seeking, in case the seeking operation changed the range start time.)
01190   char* rangeHeader;
01191   if (!sawRangeHeader) {
01192     buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back
01193   } else if (rangeEnd == 0.0 && scale >= 0.0) {
01194     sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
01195   } else {
01196     sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
01197   }
01198   rangeHeader = strDup(buf);
01199 
01200   // Now, start streaming:
01201   for (i = 0; i < fNumStreamStates; ++i) {
01202     if (subsession == NULL /* means: aggregated operation */
01203         || subsession == fStreamStates[i].subsession) {
01204       unsigned short rtpSeqNum = 0;
01205       unsigned rtpTimestamp = 0;
01206       fStreamStates[i].subsession->startStream(fOurSessionId,
01207                                                fStreamStates[i].streamToken,
01208                                                (TaskFunc*)noteClientLiveness, this,
01209                                                rtpSeqNum, rtpTimestamp,
01210                                                handleAlternativeRequestByte, this);
01211       const char *urlSuffix = fStreamStates[i].subsession->trackId();
01212       char* prevRTPInfo = rtpInfo;
01213       unsigned rtpInfoSize = rtpInfoFmtSize
01214         + strlen(prevRTPInfo)
01215         + 1
01216         + rtspURLSize + strlen(urlSuffix)
01217         + 5 /*max unsigned short len*/
01218         + 10 /*max unsigned (32-bit) len*/
01219         + 2 /*allows for trailing \r\n at final end of string*/;
01220       rtpInfo = new char[rtpInfoSize];
01221       sprintf(rtpInfo, rtpInfoFmt,
01222               prevRTPInfo,
01223               numRTPInfoItems++ == 0 ? "" : ",",
01224               rtspURL, urlSuffix,
01225               rtpSeqNum,
01226               rtpTimestamp
01227               );
01228       delete[] prevRTPInfo;
01229     }
01230   }
01231   if (numRTPInfoItems == 0) {
01232     rtpInfo[0] = '\0';
01233   } else {
01234     unsigned rtpInfoLen = strlen(rtpInfo);
01235     rtpInfo[rtpInfoLen] = '\r';
01236     rtpInfo[rtpInfoLen+1] = '\n';
01237     rtpInfo[rtpInfoLen+2] = '\0';
01238   }
01239 
01240   // Fill in the response:
01241   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01242            "RTSP/1.0 200 OK\r\n"
01243            "CSeq: %s\r\n"
01244            "%s"
01245            "%s"
01246            "%s"
01247            "Session: %08X\r\n"
01248            "%s\r\n",
01249            cseq,
01250            dateHeader(),
01251            scaleHeader,
01252            rangeHeader,
01253            fOurSessionId,
01254            rtpInfo);
01255   delete[] rtpInfo; delete[] rangeHeader;
01256   delete[] scaleHeader; delete[] rtspURL;
01257 }
01258 
01259 void RTSPServer::RTSPClientSession
01260   ::handleCmd_PAUSE(ServerMediaSubsession* subsession, char const* cseq) {
01261   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01262     if (subsession == NULL /* means: aggregated operation */
01263         || subsession == fStreamStates[i].subsession) {
01264       fStreamStates[i].subsession->pauseStream(fOurSessionId,
01265                                                fStreamStates[i].streamToken);
01266     }
01267   }
01268   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01269            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
01270            cseq, dateHeader(), fOurSessionId);
01271 }
01272 
01273 void RTSPServer::RTSPClientSession
01274 ::handleCmd_GET_PARAMETER(ServerMediaSubsession* /*subsession*/, char const* cseq,
01275                           char const* /*fullRequestStr*/) {
01276   // By default, we implement "GET_PARAMETER" just as a 'keep alive', and send back an empty response.
01277   // (If you want to handle "GET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01278   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01279   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01280            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
01281            cseq, dateHeader(), fOurSessionId);
01282 }
01283 
01284 void RTSPServer::RTSPClientSession
01285 ::handleCmd_SET_PARAMETER(ServerMediaSubsession* /*subsession*/, char const* cseq,
01286                           char const* /*fullRequestStr*/) {
01287   // By default, we implement "SET_PARAMETER" just as a 'keep alive', and send back an empty response.
01288   // (If you want to handle "SET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01289   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01290   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01291            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
01292            cseq, dateHeader(), fOurSessionId);
01293 }
01294 
01295 static void lookForHeader(char const* headerName, char const* source, unsigned sourceLen, char* resultStr, unsigned resultMaxSize) {
01296   resultStr[0] = '\0';  // by default, return an empty string
01297   unsigned headerNameLen = strlen(headerName);
01298   for (int i = 0; i < (int)(sourceLen-headerNameLen); ++i) {
01299     if (strncmp(&source[i], headerName, headerNameLen) == 0 && source[i+headerNameLen] == ':') {
01300       // We found the header.  Skip over any whitespace, then copy the rest of the line to "resultStr":
01301       for (i += headerNameLen+1; i < (int)sourceLen && (source[i] == ' ' || source[i] == '\t'); ++i) {}
01302       for (unsigned j = i; j < sourceLen; ++j) {
01303         if (source[j] == '\r' || source[j] == '\n') {
01304           // We've found the end of the line.  Copy it to the result (if it will fit):
01305           if (j-i+1 > resultMaxSize) break;
01306           char const* resultSource = &source[i];
01307           char const* resultSourceEnd = &source[j];
01308           while (resultSource < resultSourceEnd) *resultStr++ = *resultSource++;
01309           *resultStr = '\0';
01310           break;
01311         }
01312       }
01313     }
01314   }
01315 }
01316 
01317 Boolean RTSPServer::RTSPClientSession::parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize,
01318                                                               char* urlSuffix, unsigned urlSuffixMaxSize,
01319                                                               char* sessionCookie, unsigned sessionCookieMaxSize,
01320                                                               char* acceptStr, unsigned acceptStrMaxSize) {
01321   // Check for the limited HTTP requests that we expect for specifying RTSP-over-HTTP tunneling.
01322   // This parser is currently rather dumb; it should be made smarter #####
01323   char const* reqStr = (char const*)fRequestBuffer;
01324   unsigned const reqStrSize = fRequestBytesAlreadySeen;
01325 
01326   // Read everything up to the first space as the command name:
01327   Boolean parseSucceeded = False;
01328   unsigned i;
01329   for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) {
01330     char c = reqStr[i];
01331     if (c == ' ' || c == '\t') {
01332       parseSucceeded = True;
01333       break;
01334     }
01335 
01336     resultCmdName[i] = c;
01337   }
01338   resultCmdName[i] = '\0';
01339   if (!parseSucceeded) return False;
01340 
01341   // Look for the string "HTTP/", before the first \r or \n:
01342   parseSucceeded = False;
01343   for (; i < reqStrSize-5 && reqStr[i] != '\r' && reqStr[i] != '\n'; ++i) {
01344     if (reqStr[i] == 'H' && reqStr[i+1] == 'T' && reqStr[i+2]== 'T' && reqStr[i+3]== 'P' && reqStr[i+4]== '/') {
01345       i += 5; // to advance past the "HTTP/"
01346       parseSucceeded = True;
01347       break;
01348     }
01349   }
01350   if (!parseSucceeded) return False;
01351 
01352   // Get the 'URL suffix' that occurred before this:
01353   unsigned k = i-6;
01354   while (k > 0 && reqStr[k] == ' ') --k; // back up over white space
01355   unsigned j = k;
01356   while (j > 0 && reqStr[j] != ' ' && reqStr[j] != '/') --j;
01357   // The URL suffix is in position (j,k]:
01358   if (k - j + 1 > urlSuffixMaxSize) return False; // there's no room> 
01359   unsigned n = 0;
01360   while (++j <= k) urlSuffix[n++] = reqStr[j];
01361   urlSuffix[n] = '\0';
01362 
01363   // Look for various headers that we're interested in:
01364   lookForHeader("x-sessioncookie", &reqStr[i], reqStrSize-i, sessionCookie, sessionCookieMaxSize);
01365   lookForHeader("Accept", &reqStr[i], reqStrSize-i, acceptStr, acceptStrMaxSize);
01366 
01367   return True;
01368 }
01369 
01370 void RTSPServer::RTSPClientSession::handleHTTPCmd_notSupported() {
01371   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01372            "HTTP/1.0 405 Method Not Allowed\r\n%s\r\n\r\n",
01373            dateHeader());
01374 }
01375 
01376 void RTSPServer::RTSPClientSession::handleHTTPCmd_notFound() {
01377   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01378            "HTTP/1.0 404 Not Found\r\n%s\r\n\r\n",
01379            dateHeader());
01380 }
01381 
01382 void RTSPServer::RTSPClientSession::handleHTTPCmd_TunnelingGET(char const* sessionCookie) {
01383   // Record ourself as having this 'session cookie', so that a subsequent HTTP "POST" command (with the same 'session cookie')
01384   // can find us:
01385   if (fOurServer.fClientSessionsForHTTPTunneling == NULL) {
01386     fOurServer.fClientSessionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
01387   }
01388   fOurServer.fClientSessionsForHTTPTunneling->Add(sessionCookie, (void*)this);
01389 #ifdef DEBUG
01390   fprintf(stderr, "Handled HTTP \"GET\" request (client output socket: %d)\n", fClientOutputSocket);
01391 #endif
01392 
01393   // Construct our response:
01394   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01395            "HTTP/1.0 200 OK\r\n"
01396            "Date: Thu, 19 Aug 1982 18:30:00 GMT\r\n"
01397            "Cache-Control: no-cache\r\n"
01398            "Pragma: no-cache\r\n"
01399            "Content-Type: application/x-rtsp-tunnelled\r\n"
01400            "\r\n");
01401 }
01402 
01403 Boolean RTSPServer::RTSPClientSession
01404 ::handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize) {
01405   // Use the "sessionCookie" string to look up the separate "RTSPClientSession" object that should have been used to handle
01406   // an earlier HTTP "GET" request:
01407   RTSPServer::RTSPClientSession* prevClientSession
01408     = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessionsForHTTPTunneling->Lookup(sessionCookie));
01409   if (prevClientSession == NULL) {
01410     // There was no previous HTTP "GET" request; treat this "POST" request as bad:
01411     handleHTTPCmd_notSupported();
01412     fSessionIsActive = False; // triggers deletion of ourself
01413     return False;
01414   }
01415 #ifdef DEBUG
01416   fprintf(stderr, "Handled HTTP \"POST\" request (client input socket: %d)\n", fClientInputSocket);
01417 #endif
01418 
01419   // Change the previous "RTSPClientSession" object's input socket to ours.  It will be used for subsequent requests:
01420   prevClientSession->changeClientInputSocket(fClientInputSocket, extraData, extraDataSize);
01421   fClientInputSocket = fClientOutputSocket = -1; // so the socket doesn't get closed when we get deleted
01422   return True;
01423 }
01424 
01425 void RTSPServer::RTSPClientSession::handleHTTPCmd_StreamingGET(char const* /*urlSuffix*/, char const* /*fullRequestStr*/) {
01426   // By default, we don't support requests to access streams via HTTP:
01427   handleHTTPCmd_notSupported();
01428 }
01429 
01430 static Boolean parseAuthorizationHeader(char const* buf,
01431                                         char const*& username,
01432                                         char const*& realm,
01433                                         char const*& nonce, char const*& uri,
01434                                         char const*& response) {
01435   // Initialize the result parameters to default values:
01436   username = realm = nonce = uri = response = NULL;
01437 
01438   // First, find "Authorization:"
01439   while (1) {
01440     if (*buf == '\0') return False; // not found
01441     if (_strncasecmp(buf, "Authorization: Digest ", 22) == 0) break;
01442     ++buf;
01443   }
01444 
01445   // Then, run through each of the fields, looking for ones we handle:
01446   char const* fields = buf + 22;
01447   while (*fields == ' ') ++fields;
01448   char* parameter = strDupSize(fields);
01449   char* value = strDupSize(fields);
01450   while (1) {
01451     value[0] = '\0';
01452     if (sscanf(fields, "%[^=]=\"%[^\"]\"", parameter, value) != 2 &&
01453         sscanf(fields, "%[^=]=\"\"", parameter) != 1) {
01454       break;
01455     }
01456     if (strcmp(parameter, "username") == 0) {
01457       username = strDup(value);
01458     } else if (strcmp(parameter, "realm") == 0) {
01459       realm = strDup(value);
01460     } else if (strcmp(parameter, "nonce") == 0) {
01461       nonce = strDup(value);
01462     } else if (strcmp(parameter, "uri") == 0) {
01463       uri = strDup(value);
01464     } else if (strcmp(parameter, "response") == 0) {
01465       response = strDup(value);
01466     }
01467 
01468     fields += strlen(parameter) + 2 /*="*/ + strlen(value) + 1 /*"*/;
01469     while (*fields == ',' || *fields == ' ') ++fields;
01470         // skip over any separating ',' and ' ' chars
01471     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
01472   }
01473   delete[] parameter; delete[] value;
01474   return True;
01475 }
01476 
01477 Boolean RTSPServer::RTSPClientSession
01478 ::authenticationOK(char const* cmdName, char const* cseq,
01479                    char const* urlSuffix, char const* fullRequestStr) {
01480 
01481   if (!fOurServer.specialClientAccessCheck(fClientInputSocket, fClientAddr, urlSuffix)) {
01482     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01483              "RTSP/1.0 401 Unauthorized\r\n"
01484              "CSeq: %s\r\n"
01485              "%s"
01486              "\r\n",
01487              cseq, dateHeader());
01488     return False;
01489   }
01490 
01491   // If we weren't set up with an authentication database, we're OK:
01492   if (fOurServer.fAuthDB == NULL) return True;
01493 
01494   char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
01495   char const* uri = NULL; char const* response = NULL;
01496   Boolean success = False;
01497 
01498   do {
01499     // To authenticate, we first need to have a nonce set up
01500     // from a previous attempt:
01501     if (fCurrentAuthenticator.nonce() == NULL) break;
01502 
01503     // Next, the request needs to contain an "Authorization:" header,
01504     // containing a username, (our) realm, (our) nonce, uri,
01505     // and response string:
01506     if (!parseAuthorizationHeader(fullRequestStr,
01507                                   username, realm, nonce, uri, response)
01508         || username == NULL
01509         || realm == NULL || strcmp(realm, fCurrentAuthenticator.realm()) != 0
01510         || nonce == NULL || strcmp(nonce, fCurrentAuthenticator.nonce()) != 0
01511         || uri == NULL || response == NULL) {
01512       break;
01513     }
01514 
01515     // Next, the username has to be known to us:
01516     char const* password = fOurServer.fAuthDB->lookupPassword(username);
01517 #ifdef DEBUG
01518     fprintf(stderr, "lookupPassword(%s) returned password %s\n", username, password);
01519 #endif
01520     if (password == NULL) break;
01521     fCurrentAuthenticator.
01522       setUsernameAndPassword(username, password,
01523                              fOurServer.fAuthDB->passwordsAreMD5());
01524 
01525     // Finally, compute a digest response from the information that we have,
01526     // and compare it to the one that we were given:
01527     char const* ourResponse
01528       = fCurrentAuthenticator.computeDigestResponse(cmdName, uri);
01529     success = (strcmp(ourResponse, response) == 0);
01530     fCurrentAuthenticator.reclaimDigestResponse(ourResponse);
01531   } while (0);
01532 
01533   delete[] (char*)username; delete[] (char*)realm; delete[] (char*)nonce;
01534   delete[] (char*)uri; delete[] (char*)response;
01535   if (success) return True;
01536 
01537   // If we get here, there was some kind of authentication failure.
01538   // Send back a "401 Unauthorized" response, with a new random nonce:
01539   fCurrentAuthenticator.setRealmAndRandomNonce(fOurServer.fAuthDB->realm());
01540   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01541            "RTSP/1.0 401 Unauthorized\r\n"
01542            "CSeq: %s\r\n"
01543            "%s"
01544            "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n\r\n",
01545            cseq,
01546            dateHeader(),
01547            fCurrentAuthenticator.realm(), fCurrentAuthenticator.nonce());
01548   return False;
01549 }
01550 
01551 void RTSPServer::RTSPClientSession::noteLiveness() {
01552 #ifdef DEBUG
01553   fprintf(stderr, "Liveness indication from client at %s\n", AddressString(fClientAddr).val());
01554 #endif
01555   if (fOurServer.fReclamationTestSeconds > 0) {
01556     envir().taskScheduler()
01557       .rescheduleDelayedTask(fLivenessCheckTask,
01558                              fOurServer.fReclamationTestSeconds*1000000,
01559                              (TaskFunc*)livenessTimeoutTask, this);
01560   }
01561 }
01562 
01563 void RTSPServer::RTSPClientSession
01564 ::noteClientLiveness(RTSPClientSession* clientSession) {
01565   clientSession->noteLiveness();
01566 }
01567 
01568 void RTSPServer::RTSPClientSession
01569 ::livenessTimeoutTask(RTSPClientSession* clientSession) {
01570   // If this gets called, the client session is assumed to have timed out,
01571   // so delete it:
01572 #ifdef DEBUG
01573   fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", AddressString(clientSession->fClientAddr).val());
01574 #endif
01575   delete clientSession;
01576 }
01577 
01578 RTSPServer::RTSPClientSession*
01579 RTSPServer::createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) {
01580   return new RTSPClientSession(*this, sessionId, clientSocket, clientAddr);
01581 }
01582 
01583 void RTSPServer::RTSPClientSession
01584 ::changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize) {
01585   envir().taskScheduler().turnOffBackgroundReadHandling(fClientInputSocket);
01586   fClientInputSocket = newSocketNum;
01587   envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
01588      (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
01589 
01590   // Also write any extra data to our buffer, and handle it:
01591   if (extraDataSize > 0 && extraDataSize <= fRequestBufferBytesLeft/*sanity check; should always be true*/) {
01592     unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
01593     for (unsigned i = 0; i < extraDataSize; ++i) {
01594       ptr[i] = extraData[i];
01595     }
01596     handleRequestBytes(extraDataSize);
01597   }
01598 }
01599 
01600 
01602 
01603 RTSPServer::ServerMediaSessionIterator
01604 ::ServerMediaSessionIterator(RTSPServer& server)
01605   : fOurIterator((server.fServerMediaSessions == NULL)
01606                  ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
01607 }
01608 
01609 RTSPServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
01610   delete fOurIterator;
01611 }
01612 
01613 ServerMediaSession* RTSPServer::ServerMediaSessionIterator::next() {
01614   if (fOurIterator == NULL) return NULL;
01615 
01616   char const* key; // dummy
01617   return (ServerMediaSession*)(fOurIterator->next(key));
01618 }
01619 
01620 
01622 
01623 UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm,
01624                                                        Boolean passwordsAreMD5)
01625   : fTable(HashTable::create(STRING_HASH_KEYS)),
01626     fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)),
01627     fPasswordsAreMD5(passwordsAreMD5) {
01628 }
01629 
01630 UserAuthenticationDatabase::~UserAuthenticationDatabase() {
01631   delete[] fRealm;
01632 
01633   // Delete the allocated 'password' strings that we stored in the table, and then the table itself:
01634   char* password;
01635   while ((password = (char*)fTable->RemoveNext()) != NULL) {
01636     delete[] password;
01637   }
01638   delete fTable;
01639 }
01640 
01641 void UserAuthenticationDatabase::addUserRecord(char const* username,
01642                                                char const* password) {
01643   fTable->Add(username, (void*)(strDup(password)));
01644 }
01645 
01646 void UserAuthenticationDatabase::removeUserRecord(char const* username) {
01647   char* password = (char*)(fTable->Lookup(username));
01648   fTable->Remove(username);
01649   delete[] password;
01650 }
01651 
01652 char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
01653   return (char const*)(fTable->Lookup(username));
01654 }

Generated on Thu May 17 07:11:47 2012 for live by  doxygen 1.5.2