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-2010 Live Networks, Inc.  All rights reserved.
00018 // A RTSP server
00019 // Implementation
00020 
00021 #include "RTSPServer.hh"
00022 #include "RTSPCommon.hh"
00023 #include <GroupsockHelper.hh>
00024 
00025 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00026 #else
00027 #include <signal.h>
00028 #define USE_SIGNALS 1
00029 #endif
00030 #include <time.h> // for "strftime()" and "gmtime()"
00031 
00033 
00034 RTSPServer*
00035 RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
00036                       UserAuthenticationDatabase* authDatabase,
00037                       unsigned reclamationTestSeconds) {
00038   int ourSocket = -1;
00039 
00040   do {
00041     ourSocket = setUpOurSocket(env, ourPort);
00042     if (ourSocket == -1) break;
00043 
00044     return new RTSPServer(env, ourSocket, ourPort, authDatabase,
00045                           reclamationTestSeconds);
00046   } while (0);
00047 
00048   if (ourSocket != -1) ::closeSocket(ourSocket);
00049   return NULL;
00050 }
00051 
00052 Boolean RTSPServer::lookupByName(UsageEnvironment& env,
00053                                  char const* name,
00054                                  RTSPServer*& resultServer) {
00055   resultServer = NULL; // unless we succeed
00056 
00057   Medium* medium;
00058   if (!Medium::lookupByName(env, name, medium)) return False;
00059 
00060   if (!medium->isRTSPServer()) {
00061     env.setResultMsg(name, " is not a RTSP server");
00062     return False;
00063   }
00064 
00065   resultServer = (RTSPServer*)medium;
00066   return True;
00067 }
00068 
00069 void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
00070   if (serverMediaSession == NULL) return;
00071 
00072   char const* sessionName = serverMediaSession->streamName();
00073   if (sessionName == NULL) sessionName = "";
00074   ServerMediaSession* existingSession
00075     = (ServerMediaSession*)
00076     (fServerMediaSessions->Add(sessionName,
00077                                (void*)serverMediaSession));
00078   removeServerMediaSession(existingSession); // if any
00079 }
00080 
00081 ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
00082   return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
00083 }
00084 
00085 void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
00086   if (serverMediaSession == NULL) return;
00087 
00088   fServerMediaSessions->Remove(serverMediaSession->streamName());
00089   if (serverMediaSession->referenceCount() == 0) {
00090     Medium::close(serverMediaSession);
00091   } else {
00092     serverMediaSession->deleteWhenUnreferenced() = True;
00093   }
00094 }
00095 
00096 void RTSPServer::removeServerMediaSession(char const* streamName) {
00097   removeServerMediaSession(lookupServerMediaSession(streamName));
00098 }
00099 
00100 char* RTSPServer::rtspURLPrefix(int clientSocket) const {
00101   struct sockaddr_in ourAddress;
00102   if (clientSocket < 0) {
00103     // Use our default IP address in the URL:
00104     ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0
00105       ? ReceivingInterfaceAddr
00106       : ourIPAddress(envir()); // hack
00107   } else {
00108     SOCKLEN_T namelen = sizeof ourAddress;
00109     getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen);
00110   }
00111 
00112   char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"
00113 
00114   portNumBits portNumHostOrder = ntohs(fServerPort.num());
00115   if (portNumHostOrder == 554 /* the default port number */) {
00116     sprintf(urlBuffer, "rtsp://%s/", our_inet_ntoa(ourAddress.sin_addr));
00117   } else {
00118     sprintf(urlBuffer, "rtsp://%s:%hu/",
00119             our_inet_ntoa(ourAddress.sin_addr), portNumHostOrder);
00120   }
00121 
00122   return strDup(urlBuffer);
00123 }
00124 
00125 char* RTSPServer
00126 ::rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket) const {
00127   char* urlPrefix = rtspURLPrefix(clientSocket);
00128   char const* sessionName = serverMediaSession->streamName();
00129 
00130   char* resultURL = new char[strlen(urlPrefix) + strlen(sessionName) + 1];
00131   sprintf(resultURL, "%s%s", urlPrefix, sessionName);
00132 
00133   delete[] urlPrefix;
00134   return resultURL;
00135 }
00136 
00137 #define LISTEN_BACKLOG_SIZE 20
00138 
00139 int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
00140   int ourSocket = -1;
00141 
00142   do {
00143     ourSocket = setupStreamSocket(env, ourPort);
00144     if (ourSocket < 0) break;
00145 
00146     // Make sure we have a big send buffer:
00147     if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
00148 
00149     // Allow multiple simultaneous connections:
00150     if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
00151       env.setResultErrMsg("listen() failed: ");
00152       break;
00153     }
00154 
00155     if (ourPort.num() == 0) {
00156       // bind() will have chosen a port for us; return it also:
00157       if (!getSourcePort(env, ourSocket, ourPort)) break;
00158     }
00159 
00160     return ourSocket;
00161   } while (0);
00162 
00163   if (ourSocket != -1) ::closeSocket(ourSocket);
00164   return -1;
00165 }
00166 
00167 Boolean RTSPServer
00168 ::specialClientAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/, char const* /*urlSuffix*/) {
00169   // default implementation
00170   return True;
00171 }
00172 
00173 RTSPServer::RTSPServer(UsageEnvironment& env,
00174                        int ourSocket, Port ourPort,
00175                        UserAuthenticationDatabase* authDatabase,
00176                        unsigned reclamationTestSeconds)
00177   : Medium(env),
00178     fServerSocket(ourSocket), fServerPort(ourPort),
00179     fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
00180     fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)) {
00181 #ifdef USE_SIGNALS
00182   // Ignore the SIGPIPE signal, so that clients on the same host that are killed
00183   // don't also kill us:
00184   signal(SIGPIPE, SIG_IGN);
00185 #endif
00186 
00187   // Arrange to handle connections from others:
00188   env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket,
00189         (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,
00190                                                    this);
00191 }
00192 
00193 RTSPServer::~RTSPServer() {
00194   // Turn off background read handling:
00195   envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket);
00196 
00197   ::closeSocket(fServerSocket);
00198 
00199   // Remove all server media sessions (they'll get deleted when they're finished):
00200   while (1) {
00201     ServerMediaSession* serverMediaSession
00202       = (ServerMediaSession*)fServerMediaSessions->RemoveNext();
00203     if (serverMediaSession == NULL) break;
00204     removeServerMediaSession(serverMediaSession);
00205   }
00206 
00207   // Finally, delete the session table itself:
00208   delete fServerMediaSessions;
00209 }
00210 
00211 Boolean RTSPServer::isRTSPServer() const {
00212   return True;
00213 }
00214 
00215 void RTSPServer::incomingConnectionHandler(void* instance, int /*mask*/) {
00216   RTSPServer* server = (RTSPServer*)instance;
00217   server->incomingConnectionHandler1();
00218 }
00219 
00220 void RTSPServer::incomingConnectionHandler1() {
00221   struct sockaddr_in clientAddr;
00222   SOCKLEN_T clientAddrLen = sizeof clientAddr;
00223   int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr,
00224                             &clientAddrLen);
00225   if (clientSocket < 0) {
00226     int err = envir().getErrno();
00227     if (err != EWOULDBLOCK) {
00228         envir().setResultErrMsg("accept() failed: ");
00229     }
00230     return;
00231   }
00232   makeSocketNonBlocking(clientSocket);
00233   increaseSendBufferTo(envir(), clientSocket, 50*1024);
00234 
00235 #if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
00236   envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << '\n';
00237 #endif
00238 
00239   // Create a new object for this RTSP session.
00240   // (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
00241   //  a collision; the probability of two concurrent sessions getting the same session id is very low.)
00242   unsigned sessionId = (unsigned)our_random();
00243   (void)createNewClientSession(sessionId, clientSocket, clientAddr);
00244 }
00245 
00246 
00248 
00249 RTSPServer::RTSPClientSession
00250 ::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId,
00251               int clientSocket, struct sockaddr_in clientAddr)
00252   : fOurServer(ourServer), fOurSessionId(sessionId),
00253     fOurServerMediaSession(NULL),
00254     fClientSocket(clientSocket), fClientAddr(clientAddr),
00255     fLivenessCheckTask(NULL),
00256     fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
00257     fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) {
00258   // Arrange to handle incoming requests:
00259   resetRequestBuffer();
00260   envir().taskScheduler().turnOnBackgroundReadHandling(fClientSocket,
00261      (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00262   noteLiveness();
00263 }
00264 
00265 RTSPServer::RTSPClientSession::~RTSPClientSession() {
00266   // Turn off any liveness checking:
00267   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
00268 
00269   // Turn off background read handling:
00270   envir().taskScheduler().turnOffBackgroundReadHandling(fClientSocket);
00271 
00272   ::closeSocket(fClientSocket);
00273 
00274   reclaimStreamStates();
00275 
00276   if (fOurServerMediaSession != NULL) {
00277     fOurServerMediaSession->decrementReferenceCount();
00278     if (fOurServerMediaSession->referenceCount() == 0
00279         && fOurServerMediaSession->deleteWhenUnreferenced()) {
00280       fOurServer.removeServerMediaSession(fOurServerMediaSession);
00281     }
00282   }
00283 }
00284 
00285 void RTSPServer::RTSPClientSession::reclaimStreamStates() {
00286   for (unsigned i = 0; i < fNumStreamStates; ++i) {
00287     if (fStreamStates[i].subsession != NULL) {
00288       fStreamStates[i].subsession->deleteStream(fOurSessionId,
00289                                                 fStreamStates[i].streamToken);
00290     }
00291   }
00292   delete[] fStreamStates; fStreamStates = NULL;
00293   fNumStreamStates = 0;
00294 }
00295 
00296 void RTSPServer::RTSPClientSession::resetRequestBuffer() {
00297   fRequestBytesAlreadySeen = 0;
00298   fRequestBufferBytesLeft = sizeof fRequestBuffer;
00299   fLastCRLF = &fRequestBuffer[-3]; // hack
00300 }
00301 
00302 void RTSPServer::RTSPClientSession::incomingRequestHandler(void* instance, int /*mask*/) {
00303   RTSPClientSession* session = (RTSPClientSession*)instance;
00304   session->incomingRequestHandler1();
00305 }
00306 
00307 void RTSPServer::RTSPClientSession::incomingRequestHandler1() {
00308   struct sockaddr_in dummy; // 'from' address, meaningless in this case
00309 
00310   int bytesRead = readSocket(envir(), fClientSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
00311   handleRequestBytes(bytesRead);
00312 }
00313 
00314 void RTSPServer::RTSPClientSession::handleAlternativeRequestByte(void* instance, u_int8_t requestByte) {
00315   RTSPClientSession* session = (RTSPClientSession*)instance;
00316   session->handleAlternativeRequestByte1(requestByte);
00317 }
00318 
00319 void RTSPServer::RTSPClientSession::handleAlternativeRequestByte1(u_int8_t requestByte) {
00320   // Add this character to our buffer; then try to handle the data that we have buffered so far:
00321   if (fRequestBufferBytesLeft == 0|| fRequestBytesAlreadySeen >= RTSP_BUFFER_SIZE) return;
00322   fRequestBuffer[fRequestBytesAlreadySeen] = requestByte;
00323   handleRequestBytes(1);
00324 }
00325 
00326 void RTSPServer::RTSPClientSession::handleRequestBytes(int newBytesRead) {
00327   noteLiveness();
00328 
00329   if (newBytesRead <= 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
00330     // Either the client socket has died, or the request was too big for us.
00331     // Terminate this connection:
00332 #ifdef DEBUG
00333     fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
00334 #endif
00335     delete this;
00336     return;
00337   }
00338 
00339   Boolean endOfMsg = False;
00340   unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
00341 #ifdef DEBUG
00342   ptr[newBytesRead] = '\0';
00343   fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes:%s\n", this, newBytesRead, ptr);
00344 #endif
00345 
00346   // Look for the end of the message: <CR><LF><CR><LF>
00347   unsigned char *tmpPtr = ptr;
00348   if (fRequestBytesAlreadySeen > 0) --tmpPtr;
00349       // in case the last read ended with a <CR>
00350   while (tmpPtr < &ptr[newBytesRead-1]) {
00351     if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
00352       if (tmpPtr - fLastCRLF == 2) { // This is it:
00353         endOfMsg = True;
00354         break;
00355       }
00356       fLastCRLF = tmpPtr;
00357     }
00358     ++tmpPtr;
00359   }
00360 
00361   fRequestBufferBytesLeft -= newBytesRead;
00362   fRequestBytesAlreadySeen += newBytesRead;
00363 
00364   if (!endOfMsg) return; // subsequent reads will be needed to complete the request
00365 
00366   // Parse the request string into command name and 'CSeq',
00367   // then handle the command:
00368   fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
00369   char cmdName[RTSP_PARAM_STRING_MAX];
00370   char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00371   char urlSuffix[RTSP_PARAM_STRING_MAX];
00372   char cseq[RTSP_PARAM_STRING_MAX];
00373   if (!parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
00374                               cmdName, sizeof cmdName,
00375                               urlPreSuffix, sizeof urlPreSuffix,
00376                               urlSuffix, sizeof urlSuffix,
00377                               cseq, sizeof cseq)) {
00378 #ifdef DEBUG
00379     fprintf(stderr, "parseRTSPRequestString() failed!\n");
00380 #endif
00381     handleCmd_bad(cseq);
00382   } else {
00383 #ifdef DEBUG
00384     fprintf(stderr, "parseRTSPRequestString() returned cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\"\n", cmdName, urlPreSuffix, urlSuffix);
00385 #endif
00386     if (strcmp(cmdName, "OPTIONS") == 0) {
00387       handleCmd_OPTIONS(cseq);
00388     } else if (strcmp(cmdName, "DESCRIBE") == 0) {
00389       handleCmd_DESCRIBE(cseq, urlSuffix, (char const*)fRequestBuffer);
00390     } else if (strcmp(cmdName, "SETUP") == 0) {
00391       handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00392     } else if (strcmp(cmdName, "TEARDOWN") == 0
00393                || strcmp(cmdName, "PLAY") == 0
00394                || strcmp(cmdName, "PAUSE") == 0
00395                || strcmp(cmdName, "GET_PARAMETER") == 0
00396                || strcmp(cmdName, "SET_PARAMETER") == 0) {
00397       handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq,
00398                               (char const*)fRequestBuffer);
00399     } else {
00400       handleCmd_notSupported(cseq);
00401     }
00402   }
00403 
00404 #ifdef DEBUG
00405   fprintf(stderr, "sending response: %s", fResponseBuffer);
00406 #endif
00407   send(fClientSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00408 
00409   if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
00410     // The client has asked for streaming to commence now, rather than after a
00411     // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
00412     handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
00413                             (char const*)fRequestBuffer);
00414   }
00415 
00416   resetRequestBuffer(); // to prepare for any subsequent request
00417   if (!fSessionIsActive) delete this;
00418 }
00419 
00420 // Handler routines for specific RTSP commands:
00421 
00422 // Generate a "Date:" header for use in a RTSP response:
00423 static char const* dateHeader() {
00424   static char buf[200];
00425 #if !defined(_WIN32_WCE)
00426   time_t tt = time(NULL);
00427   strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT\r\n", gmtime(&tt));
00428 #else
00429   // WinCE apparently doesn't have "time()", "strftime()", or "gmtime()",
00430   // so generate the "Date:" header a different, WinCE-specific way.
00431   // (Thanks to Pierre l'Hussiez for this code)
00432   SYSTEMTIME SystemTime;
00433   GetSystemTime(&SystemTime);
00434   WCHAR dateFormat[] = L"ddd, MMM dd yyyy";
00435   WCHAR timeFormat[] = L"HH:mm:ss GMT\r\n";
00436   WCHAR inBuf[200];
00437   DWORD locale = LOCALE_NEUTRAL;
00438 
00439   int ret = GetDateFormat(locale, 0, &SystemTime,
00440                           (LPTSTR)dateFormat, (LPTSTR)inBuf, sizeof inBuf);
00441   inBuf[ret - 1] = ' ';
00442   ret = GetTimeFormat(locale, 0, &SystemTime,
00443                       (LPTSTR)timeFormat,
00444                       (LPTSTR)inBuf + ret, (sizeof inBuf) - ret);
00445   wcstombs(buf, inBuf, wcslen(inBuf));
00446 #endif
00447   return buf;
00448 }
00449 
00450 static char const* allowedCommandNames
00451 = "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER";
00452 
00453 void RTSPServer::RTSPClientSession::handleCmd_bad(char const* /*cseq*/) {
00454   // Don't do anything with "cseq", because it might be nonsense
00455   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00456            "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
00457            dateHeader(), allowedCommandNames);
00458 }
00459 
00460 void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) {
00461   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00462            "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
00463            cseq, dateHeader(), allowedCommandNames);
00464 }
00465 
00466 void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) {
00467   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00468            "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
00469            cseq, dateHeader());
00470   fSessionIsActive = False; // triggers deletion of ourself after responding
00471 }
00472 
00473 void RTSPServer::RTSPClientSession::handleCmd_unsupportedTransport(char const* cseq) {
00474   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00475            "RTSP/1.0 461 Unsupported Transport\r\nCSeq: %s\r\n%s\r\n",
00476            cseq, dateHeader());
00477   fSessionIsActive = False; // triggers deletion of ourself after responding
00478 }
00479 
00480 void RTSPServer::RTSPClientSession::handleCmd_OPTIONS(char const* cseq) {
00481   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00482            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
00483            cseq, dateHeader(), allowedCommandNames);
00484 }
00485 
00486 void RTSPServer::RTSPClientSession
00487 ::handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
00488                      char const* fullRequestStr) {
00489   char* sdpDescription = NULL;
00490   char* rtspURL = NULL;
00491   do {
00492       if (!authenticationOK("DESCRIBE", cseq, urlSuffix, fullRequestStr))
00493           break;
00494 
00495     // We should really check that the request contains an "Accept:" #####
00496     // for "application/sdp", because that's what we're sending back #####
00497 
00498     // Begin by looking up the "ServerMediaSession" object for the
00499     // specified "urlSuffix":
00500     ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
00501     if (session == NULL) {
00502       handleCmd_notFound(cseq);
00503       break;
00504     }
00505 
00506     // Then, assemble a SDP description for this session:
00507     sdpDescription = session->generateSDPDescription();
00508     if (sdpDescription == NULL) {
00509       // This usually means that a file name that was specified for a
00510       // "ServerMediaSubsession" does not exist.
00511       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00512                "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
00513                "CSeq: %s\r\n"
00514                "%s\r\n",
00515                cseq,
00516                dateHeader());
00517      break;
00518     }
00519     unsigned sdpDescriptionSize = strlen(sdpDescription);
00520 
00521     // Also, generate our RTSP URL, for the "Content-Base:" header
00522     // (which is necessary to ensure that the correct URL gets used in
00523     // subsequent "SETUP" requests).
00524     rtspURL = fOurServer.rtspURL(session, fClientSocket);
00525 
00526     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00527              "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
00528              "%s"
00529              "Content-Base: %s/\r\n"
00530              "Content-Type: application/sdp\r\n"
00531              "Content-Length: %d\r\n\r\n"
00532              "%s",
00533              cseq,
00534              dateHeader(),
00535              rtspURL,
00536              sdpDescriptionSize,
00537              sdpDescription);
00538   } while (0);
00539 
00540   delete[] sdpDescription;
00541   delete[] rtspURL;
00542 }
00543 
00544 typedef enum StreamingMode {
00545   RTP_UDP,
00546   RTP_TCP,
00547   RAW_UDP
00548 } StreamingMode;
00549 
00550 static void parseTransportHeader(char const* buf,
00551                                  StreamingMode& streamingMode,
00552                                  char*& streamingModeString,
00553                                  char*& destinationAddressStr,
00554                                  u_int8_t& destinationTTL,
00555                                  portNumBits& clientRTPPortNum, // if UDP
00556                                  portNumBits& clientRTCPPortNum, // if UDP
00557                                  unsigned char& rtpChannelId, // if TCP
00558                                  unsigned char& rtcpChannelId // if TCP
00559                                  ) {
00560   // Initialize the result parameters to default values:
00561   streamingMode = RTP_UDP;
00562   streamingModeString = NULL;
00563   destinationAddressStr = NULL;
00564   destinationTTL = 255;
00565   clientRTPPortNum = 0;
00566   clientRTCPPortNum = 1;
00567   rtpChannelId = rtcpChannelId = 0xFF;
00568 
00569   portNumBits p1, p2;
00570   unsigned ttl, rtpCid, rtcpCid;
00571 
00572   // First, find "Transport:"
00573   while (1) {
00574     if (*buf == '\0') return; // not found
00575     if (_strncasecmp(buf, "Transport: ", 11) == 0) break;
00576     ++buf;
00577   }
00578 
00579   // Then, run through each of the fields, looking for ones we handle:
00580   char const* fields = buf + 11;
00581   char* field = strDupSize(fields);
00582   while (sscanf(fields, "%[^;]", field) == 1) {
00583     if (strcmp(field, "RTP/AVP/TCP") == 0) {
00584       streamingMode = RTP_TCP;
00585     } else if (strcmp(field, "RAW/RAW/UDP") == 0 ||
00586                strcmp(field, "MP2T/H2221/UDP") == 0) {
00587       streamingMode = RAW_UDP;
00588       streamingModeString = strDup(field);
00589     } else if (_strncasecmp(field, "destination=", 12) == 0) {
00590       delete[] destinationAddressStr;
00591       destinationAddressStr = strDup(field+12);
00592     } else if (sscanf(field, "ttl%u", &ttl) == 1) {
00593       destinationTTL = (u_int8_t)ttl;
00594     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {
00595         clientRTPPortNum = p1;
00596         clientRTCPPortNum = p2;
00597     } else if (sscanf(field, "client_port=%hu", &p1) == 1) {
00598         clientRTPPortNum = p1;
00599         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;
00600     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
00601       rtpChannelId = (unsigned char)rtpCid;
00602       rtcpChannelId = (unsigned char)rtcpCid;
00603     }
00604 
00605     fields += strlen(field);
00606     while (*fields == ';') ++fields; // skip over separating ';' chars
00607     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
00608   }
00609   delete[] field;
00610 }
00611 
00612 static Boolean parsePlayNowHeader(char const* buf) {
00613   // Find "x-playNow:" header, if present
00614   while (1) {
00615     if (*buf == '\0') return False; // not found
00616     if (_strncasecmp(buf, "x-playNow:", 10) == 0) break;
00617     ++buf;
00618   }
00619 
00620   return True;
00621 }
00622 
00623 void RTSPServer::RTSPClientSession
00624 ::handleCmd_SETUP(char const* cseq,
00625                   char const* urlPreSuffix, char const* urlSuffix,
00626                   char const* fullRequestStr) {
00627   // "urlPreSuffix" should be the session (stream) name, and
00628   // "urlSuffix" should be the subsession (track) name.
00629   char const* streamName = urlPreSuffix;
00630   char const* trackId = urlSuffix;
00631 
00632   // Check whether we have existing session state, and, if so, whether it's
00633   // for the session that's named in "streamName".  (Note that we don't
00634   // support more than one concurrent session on the same client connection.) #####
00635   if (fOurServerMediaSession != NULL
00636       && strcmp(streamName, fOurServerMediaSession->streamName()) != 0) {
00637     fOurServerMediaSession = NULL;
00638   }
00639   if (fOurServerMediaSession == NULL) {
00640     // Set up this session's state.
00641 
00642     // Look up the "ServerMediaSession" object for the specified stream:
00643     if (streamName[0] != '\0' ||
00644         fOurServer.lookupServerMediaSession("") != NULL) { // normal case
00645     } else { // weird case: there was no track id in the URL
00646       streamName = urlSuffix;
00647       trackId = NULL;
00648     }
00649     fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
00650     if (fOurServerMediaSession == NULL) {
00651       handleCmd_notFound(cseq);
00652       return;
00653     }
00654 
00655     fOurServerMediaSession->incrementReferenceCount();
00656 
00657     // Set up our array of states for this session's subsessions (tracks):
00658     reclaimStreamStates();
00659     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
00660     for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {}
00661     fStreamStates = new struct streamState[fNumStreamStates];
00662     iter.reset();
00663     ServerMediaSubsession* subsession;
00664     for (unsigned i = 0; i < fNumStreamStates; ++i) {
00665       subsession = iter.next();
00666       fStreamStates[i].subsession = subsession;
00667       fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later
00668     }
00669   }
00670 
00671   // Look up information for the specified subsession (track):
00672   ServerMediaSubsession* subsession = NULL;
00673   unsigned streamNum;
00674   if (trackId != NULL && trackId[0] != '\0') { // normal case
00675     for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
00676       subsession = fStreamStates[streamNum].subsession;
00677       if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
00678     }
00679     if (streamNum >= fNumStreamStates) {
00680       // The specified track id doesn't exist, so this request fails:
00681       handleCmd_notFound(cseq);
00682       return;
00683     }
00684   } else {
00685     // Weird case: there was no track id in the URL.
00686     // This works only if we have only one subsession:
00687     if (fNumStreamStates != 1) {
00688       handleCmd_bad(cseq);
00689       return;
00690     }
00691     streamNum = 0;
00692     subsession = fStreamStates[streamNum].subsession;
00693   }
00694   // ASSERT: subsession != NULL
00695 
00696   // Look for a "Transport:" header in the request string,
00697   // to extract client parameters:
00698   StreamingMode streamingMode;
00699   char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
00700   char* clientsDestinationAddressStr;
00701   u_int8_t clientsDestinationTTL;
00702   portNumBits clientRTPPortNum, clientRTCPPortNum;
00703   unsigned char rtpChannelId, rtcpChannelId;
00704   parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
00705                        clientsDestinationAddressStr, clientsDestinationTTL,
00706                        clientRTPPortNum, clientRTCPPortNum,
00707                        rtpChannelId, rtcpChannelId);
00708   if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) {
00709     // TCP streaming was requested, but with no "interleaving=" fields.
00710     // (QuickTime Player sometimes does this.)  Set the RTP and RTCP channel ids to
00711     // proper values:
00712     rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
00713   }
00714   fTCPStreamIdCount += 2;
00715 
00716   Port clientRTPPort(clientRTPPortNum);
00717   Port clientRTCPPort(clientRTCPPortNum);
00718 
00719   // Next, check whether a "Range:" header is present in the request.
00720   // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
00721   double rangeStart = 0.0, rangeEnd = 0.0;
00722   fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) ||
00723                       parsePlayNowHeader(fullRequestStr);
00724 
00725   // Then, get server parameters from the 'subsession':
00726   int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1;
00727   netAddressBits destinationAddress = 0;
00728   u_int8_t destinationTTL = 255;
00729 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
00730   if (clientsDestinationAddressStr != NULL) {
00731     // Use the client-provided "destination" address.
00732     // Note: This potentially allows the server to be used in denial-of-service
00733     // attacks, so don't enable this code unless you're sure that clients are
00734     // trusted.
00735     destinationAddress = our_inet_addr(clientsDestinationAddressStr);
00736   }
00737   // Also use the client-provided TTL.
00738   destinationTTL = clientsDestinationTTL;
00739 #endif
00740   delete[] clientsDestinationAddressStr;
00741   Port serverRTPPort(0);
00742   Port serverRTCPPort(0);
00743   subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
00744                                   clientRTPPort, clientRTCPPort,
00745                                   tcpSocketNum, rtpChannelId, rtcpChannelId,
00746                                   destinationAddress, destinationTTL, fIsMulticast,
00747                                   serverRTPPort, serverRTCPPort,
00748                                   fStreamStates[streamNum].streamToken);
00749   struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
00750   char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));
00751   struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
00752   getsockname(fClientSocket, (struct sockaddr*)&sourceAddr, &namelen);
00753   char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));
00754   if (fIsMulticast) {
00755     switch (streamingMode) {
00756     case RTP_UDP:
00757         snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00758                  "RTSP/1.0 200 OK\r\n"
00759                  "CSeq: %s\r\n"
00760                  "%s"
00761                  "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
00762                  "Session: %08X\r\n\r\n",
00763                  cseq,
00764                  dateHeader(),
00765                  destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
00766                  fOurSessionId);
00767         break;
00768     case RTP_TCP:
00769         // multicast streams can't be sent via TCP
00770         handleCmd_unsupportedTransport(cseq);
00771         break;
00772     case RAW_UDP:
00773         snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00774                  "RTSP/1.0 200 OK\r\n"
00775                  "CSeq: %s\r\n"
00776                  "%s"
00777                  "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
00778                  "Session: %08X\r\n\r\n",
00779                  cseq,
00780                  dateHeader(),
00781                  streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL,
00782                  fOurSessionId);
00783         break;
00784     }
00785   } else {
00786     switch (streamingMode) {
00787     case RTP_UDP: {
00788       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00789                "RTSP/1.0 200 OK\r\n"
00790                "CSeq: %s\r\n"
00791                "%s"
00792                "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
00793                "Session: %08X\r\n\r\n",
00794                cseq,
00795                dateHeader(),
00796                destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
00797                fOurSessionId);
00798       break;
00799     }
00800     case RTP_TCP: {
00801       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00802                "RTSP/1.0 200 OK\r\n"
00803                "CSeq: %s\r\n"
00804                "%s"
00805                "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
00806                "Session: %08X\r\n\r\n",
00807                cseq,
00808                dateHeader(),
00809                destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,
00810                fOurSessionId);
00811       break;
00812     }
00813     case RAW_UDP: {
00814       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00815                "RTSP/1.0 200 OK\r\n"
00816                "CSeq: %s\r\n"
00817                "%s"
00818                "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
00819                "Session: %08X\r\n\r\n",
00820                cseq,
00821                dateHeader(),
00822                streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
00823                fOurSessionId);
00824       break;
00825     }
00826     }
00827   }
00828   delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString;
00829 }
00830 
00831 void RTSPServer::RTSPClientSession
00832 ::handleCmd_withinSession(char const* cmdName,
00833                           char const* urlPreSuffix, char const* urlSuffix,
00834                           char const* cseq, char const* fullRequestStr) {
00835   // This will either be:
00836   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
00837   //   name and "urlSuffix" is the subsession (track) name, or
00838   // - a aggregated operation, if "urlSuffix" is the session (stream) name,
00839   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix"
00840   //   is empty.
00841   // First, figure out which of these it is:
00842   if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
00843     handleCmd_notSupported(cseq);
00844     return;
00845   }
00846   ServerMediaSubsession* subsession;
00847   if (urlSuffix[0] != '\0' &&
00848       strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
00849     // Non-aggregated operation.
00850     // Look up the media subsession whose track id is "urlSuffix":
00851     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
00852     while ((subsession = iter.next()) != NULL) {
00853       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
00854     }
00855     if (subsession == NULL) { // no such track!
00856       handleCmd_notFound(cseq);
00857       return;
00858     }
00859   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
00860              strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
00861     // Aggregated operation
00862     subsession = NULL;
00863   } else { // the request doesn't match a known stream and/or track at all!
00864     handleCmd_notFound(cseq);
00865     return;
00866   }
00867 
00868   if (strcmp(cmdName, "TEARDOWN") == 0) {
00869     handleCmd_TEARDOWN(subsession, cseq);
00870   } else if (strcmp(cmdName, "PLAY") == 0) {
00871     handleCmd_PLAY(subsession, cseq, fullRequestStr);
00872   } else if (strcmp(cmdName, "PAUSE") == 0) {
00873     handleCmd_PAUSE(subsession, cseq);
00874   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
00875     handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr);
00876   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
00877     handleCmd_SET_PARAMETER(subsession, cseq, fullRequestStr);
00878   }
00879 }
00880 
00881 void RTSPServer::RTSPClientSession
00882 ::handleCmd_TEARDOWN(ServerMediaSubsession* /*subsession*/, char const* cseq) {
00883   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00884            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%s\r\n",
00885            cseq, dateHeader());
00886   fSessionIsActive = False; // triggers deletion of ourself after responding
00887 }
00888 
00889 static Boolean parseScaleHeader(char const* buf, float& scale) {
00890   // Initialize the result parameter to a default value:
00891   scale = 1.0;
00892 
00893   // First, find "Scale:"
00894   while (1) {
00895     if (*buf == '\0') return False; // not found
00896     if (_strncasecmp(buf, "Scale: ", 7) == 0) break;
00897     ++buf;
00898   }
00899 
00900   // Then, run through each of the fields, looking for ones we handle:
00901   char const* fields = buf + 7;
00902   while (*fields == ' ') ++fields;
00903   float sc;
00904   if (sscanf(fields, "%f", &sc) == 1) {
00905     scale = sc;
00906   } else {
00907     return False; // The header is malformed
00908   }
00909 
00910   return True;
00911 }
00912 
00913 void RTSPServer::RTSPClientSession
00914   ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,
00915                    char const* fullRequestStr) {
00916   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientSocket);
00917   unsigned rtspURLSize = strlen(rtspURL);
00918 
00919   // Parse the client's "Scale:" header, if any:
00920   float scale;
00921   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
00922 
00923   // Try to set the stream's scale factor to this value:
00924   if (subsession == NULL /*aggregate op*/) {
00925     fOurServerMediaSession->testScaleFactor(scale);
00926   } else {
00927     subsession->testScaleFactor(scale);
00928   }
00929 
00930   char buf[100];
00931   char* scaleHeader;
00932   if (!sawScaleHeader) {
00933     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
00934   } else {
00935     sprintf(buf, "Scale: %f\r\n", scale);
00936   }
00937   scaleHeader = strDup(buf);
00938 
00939   // Parse the client's "Range:" header, if any:
00940   double rangeStart = 0.0, rangeEnd = 0.0;
00941   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd);
00942 
00943   // Use this information, plus the stream's duration (if known), to create
00944   // our own "Range:" header, for the response:
00945   float duration = subsession == NULL /*aggregate op*/
00946     ? fOurServerMediaSession->duration() : subsession->duration();
00947   if (duration < 0.0) {
00948     // We're an aggregate PLAY, but the subsessions have different durations.
00949     // Use the largest of these durations in our header
00950     duration = -duration;
00951   }
00952 
00953   if (rangeEnd <= 0.0 || rangeEnd > duration) rangeEnd = duration;
00954   if (rangeStart < 0.0) {
00955     rangeStart = 0.0;
00956   } else if (rangeEnd > 0.0 && scale > 0.0 && rangeStart > rangeEnd) {
00957     rangeStart = rangeEnd;
00958   }
00959 
00960   char* rangeHeader;
00961   if (!sawRangeHeader) {
00962     buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back
00963   } else if (rangeEnd == 0.0 && scale >= 0.0) {
00964     sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
00965   } else {
00966     sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
00967   }
00968   rangeHeader = strDup(buf);
00969 
00970   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
00971   char const* rtpInfoFmt =
00972     "%s" // "RTP-Info:", plus any preceding rtpInfo items
00973     "%s" // comma separator, if needed
00974     "url=%s/%s"
00975     ";seq=%d"
00976     ";rtptime=%u"
00977     ;
00978   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
00979   char* rtpInfo = strDup("RTP-Info: ");
00980   unsigned i, numRTPInfoItems = 0;
00981 
00982   // Do any required seeking/scaling on each subsession, before starting streaming:
00983   for (i = 0; i < fNumStreamStates; ++i) {
00984     if (subsession == NULL /* means: aggregated operation */
00985         || subsession == fStreamStates[i].subsession) {
00986       if (sawScaleHeader) {
00987         fStreamStates[i].subsession->setStreamScale(fOurSessionId,
00988                                                     fStreamStates[i].streamToken,
00989                                                     scale);
00990       }
00991       if (sawRangeHeader) {
00992         fStreamStates[i].subsession->seekStream(fOurSessionId,
00993                                                 fStreamStates[i].streamToken,
00994                                                 rangeStart);
00995       }
00996     }
00997   }
00998 
00999   // Now, start streaming:
01000   for (i = 0; i < fNumStreamStates; ++i) {
01001     if (subsession == NULL /* means: aggregated operation */
01002         || subsession == fStreamStates[i].subsession) {
01003       unsigned short rtpSeqNum = 0;
01004       unsigned rtpTimestamp = 0;
01005       fStreamStates[i].subsession->startStream(fOurSessionId,
01006                                                fStreamStates[i].streamToken,
01007                                                (TaskFunc*)noteClientLiveness, this,
01008                                                rtpSeqNum, rtpTimestamp,
01009                                                handleAlternativeRequestByte, this);
01010       const char *urlSuffix = fStreamStates[i].subsession->trackId();
01011       char* prevRTPInfo = rtpInfo;
01012       unsigned rtpInfoSize = rtpInfoFmtSize
01013         + strlen(prevRTPInfo)
01014         + 1
01015         + rtspURLSize + strlen(urlSuffix)
01016         + 5 /*max unsigned short len*/
01017         + 10 /*max unsigned (32-bit) len*/
01018         + 2 /*allows for trailing \r\n at final end of string*/;
01019       rtpInfo = new char[rtpInfoSize];
01020       sprintf(rtpInfo, rtpInfoFmt,
01021               prevRTPInfo,
01022               numRTPInfoItems++ == 0 ? "" : ",",
01023               rtspURL, urlSuffix,
01024               rtpSeqNum,
01025               rtpTimestamp
01026               );
01027       delete[] prevRTPInfo;
01028     }
01029   }
01030   if (numRTPInfoItems == 0) {
01031     rtpInfo[0] = '\0';
01032   } else {
01033     unsigned rtpInfoLen = strlen(rtpInfo);
01034     rtpInfo[rtpInfoLen] = '\r';
01035     rtpInfo[rtpInfoLen+1] = '\n';
01036     rtpInfo[rtpInfoLen+2] = '\0';
01037   }
01038 
01039   // Fill in the response:
01040   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01041            "RTSP/1.0 200 OK\r\n"
01042            "CSeq: %s\r\n"
01043            "%s"
01044            "%s"
01045            "%s"
01046            "Session: %08X\r\n"
01047            "%s\r\n",
01048            cseq,
01049            dateHeader(),
01050            scaleHeader,
01051            rangeHeader,
01052            fOurSessionId,
01053            rtpInfo);
01054   delete[] rtpInfo; delete[] rangeHeader;
01055   delete[] scaleHeader; delete[] rtspURL;
01056 }
01057 
01058 void RTSPServer::RTSPClientSession
01059   ::handleCmd_PAUSE(ServerMediaSubsession* subsession, char const* cseq) {
01060   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01061     if (subsession == NULL /* means: aggregated operation */
01062         || subsession == fStreamStates[i].subsession) {
01063       fStreamStates[i].subsession->pauseStream(fOurSessionId,
01064                                                fStreamStates[i].streamToken);
01065     }
01066   }
01067   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01068            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
01069            cseq, dateHeader(), fOurSessionId);
01070 }
01071 
01072 void RTSPServer::RTSPClientSession
01073 ::handleCmd_GET_PARAMETER(ServerMediaSubsession* subsession, char const* cseq,
01074                           char const* /*fullRequestStr*/) {
01075   // We implement "GET_PARAMETER" just as a 'keep alive',
01076   // and send back an empty response:
01077   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01078            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
01079            cseq, dateHeader(), fOurSessionId);
01080 }
01081 
01082 void RTSPServer::RTSPClientSession
01083 ::handleCmd_SET_PARAMETER(ServerMediaSubsession* /*subsession*/, char const* cseq,
01084                           char const* /*fullRequestStr*/) {
01085   // By default, we don't implement "SET_PARAMETER":
01086   handleCmd_notSupported(cseq);
01087 }
01088 
01089 static Boolean parseAuthorizationHeader(char const* buf,
01090                                         char const*& username,
01091                                         char const*& realm,
01092                                         char const*& nonce, char const*& uri,
01093                                         char const*& response) {
01094   // Initialize the result parameters to default values:
01095   username = realm = nonce = uri = response = NULL;
01096 
01097   // First, find "Authorization:"
01098   while (1) {
01099     if (*buf == '\0') return False; // not found
01100     if (_strncasecmp(buf, "Authorization: Digest ", 22) == 0) break;
01101     ++buf;
01102   }
01103 
01104   // Then, run through each of the fields, looking for ones we handle:
01105   char const* fields = buf + 22;
01106   while (*fields == ' ') ++fields;
01107   char* parameter = strDupSize(fields);
01108   char* value = strDupSize(fields);
01109   while (1) {
01110     value[0] = '\0';
01111     if (sscanf(fields, "%[^=]=\"%[^\"]\"", parameter, value) != 2 &&
01112         sscanf(fields, "%[^=]=\"\"", parameter) != 1) {
01113       break;
01114     }
01115     if (strcmp(parameter, "username") == 0) {
01116       username = strDup(value);
01117     } else if (strcmp(parameter, "realm") == 0) {
01118       realm = strDup(value);
01119     } else if (strcmp(parameter, "nonce") == 0) {
01120       nonce = strDup(value);
01121     } else if (strcmp(parameter, "uri") == 0) {
01122       uri = strDup(value);
01123     } else if (strcmp(parameter, "response") == 0) {
01124       response = strDup(value);
01125     }
01126 
01127     fields += strlen(parameter) + 2 /*="*/ + strlen(value) + 1 /*"*/;
01128     while (*fields == ',' || *fields == ' ') ++fields;
01129         // skip over any separating ',' and ' ' chars
01130     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
01131   }
01132   delete[] parameter; delete[] value;
01133   return True;
01134 }
01135 
01136 Boolean RTSPServer::RTSPClientSession
01137 ::authenticationOK(char const* cmdName, char const* cseq,
01138                    char const* urlSuffix, char const* fullRequestStr) {
01139 
01140   if (!fOurServer.specialClientAccessCheck(fClientSocket, fClientAddr, urlSuffix)) {
01141     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01142              "RTSP/1.0 401 Unauthorized\r\n"
01143              "CSeq: %s\r\n"
01144              "%s"
01145              "\r\n",
01146              cseq, dateHeader());
01147     return False;
01148   }
01149 
01150   // If we weren't set up with an authentication database, we're OK:
01151   if (fOurServer.fAuthDB == NULL) return True;
01152 
01153   char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
01154   char const* uri = NULL; char const* response = NULL;
01155   Boolean success = False;
01156 
01157   do {
01158     // To authenticate, we first need to have a nonce set up
01159     // from a previous attempt:
01160     if (fCurrentAuthenticator.nonce() == NULL) break;
01161 
01162     // Next, the request needs to contain an "Authorization:" header,
01163     // containing a username, (our) realm, (our) nonce, uri,
01164     // and response string:
01165     if (!parseAuthorizationHeader(fullRequestStr,
01166                                   username, realm, nonce, uri, response)
01167         || username == NULL
01168         || realm == NULL || strcmp(realm, fCurrentAuthenticator.realm()) != 0
01169         || nonce == NULL || strcmp(nonce, fCurrentAuthenticator.nonce()) != 0
01170         || uri == NULL || response == NULL) {
01171       break;
01172     }
01173 
01174     // Next, the username has to be known to us:
01175     char const* password = fOurServer.fAuthDB->lookupPassword(username);
01176 #ifdef DEBUG
01177     fprintf(stderr, "lookupPassword(%s) returned password %s\n", username, password);
01178 #endif
01179     if (password == NULL) break;
01180     fCurrentAuthenticator.
01181       setUsernameAndPassword(username, password,
01182                              fOurServer.fAuthDB->passwordsAreMD5());
01183 
01184     // Finally, compute a digest response from the information that we have,
01185     // and compare it to the one that we were given:
01186     char const* ourResponse
01187       = fCurrentAuthenticator.computeDigestResponse(cmdName, uri);
01188     success = (strcmp(ourResponse, response) == 0);
01189     fCurrentAuthenticator.reclaimDigestResponse(ourResponse);
01190   } while (0);
01191 
01192   delete[] (char*)username; delete[] (char*)realm; delete[] (char*)nonce;
01193   delete[] (char*)uri; delete[] (char*)response;
01194   if (success) return True;
01195 
01196   // If we get here, there was some kind of authentication failure.
01197   // Send back a "401 Unauthorized" response, with a new random nonce:
01198   fCurrentAuthenticator.setRealmAndRandomNonce(fOurServer.fAuthDB->realm());
01199   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01200            "RTSP/1.0 401 Unauthorized\r\n"
01201            "CSeq: %s\r\n"
01202            "%s"
01203            "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n\r\n",
01204            cseq,
01205            dateHeader(),
01206            fCurrentAuthenticator.realm(), fCurrentAuthenticator.nonce());
01207   return False;
01208 }
01209 
01210 void RTSPServer::RTSPClientSession::noteLiveness() {
01211   if (fOurServer.fReclamationTestSeconds > 0) {
01212     envir().taskScheduler()
01213       .rescheduleDelayedTask(fLivenessCheckTask,
01214                              fOurServer.fReclamationTestSeconds*1000000,
01215                              (TaskFunc*)livenessTimeoutTask, this);
01216   }
01217 }
01218 
01219 void RTSPServer::RTSPClientSession
01220 ::noteClientLiveness(RTSPClientSession* clientSession) {
01221   clientSession->noteLiveness();
01222 }
01223 
01224 void RTSPServer::RTSPClientSession
01225 ::livenessTimeoutTask(RTSPClientSession* clientSession) {
01226   // If this gets called, the client session is assumed to have timed out,
01227   // so delete it:
01228 #ifdef DEBUG
01229   fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr));
01230 #endif
01231   delete clientSession;
01232 }
01233 
01234 RTSPServer::RTSPClientSession*
01235 RTSPServer::createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) {
01236   return new RTSPClientSession(*this, sessionId, clientSocket, clientAddr);
01237 }
01238 
01239 
01241 
01242 RTSPServer::ServerMediaSessionIterator
01243 ::ServerMediaSessionIterator(RTSPServer& server)
01244   : fOurIterator((server.fServerMediaSessions == NULL)
01245                  ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
01246 }
01247 
01248 RTSPServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
01249   delete fOurIterator;
01250 }
01251 
01252 ServerMediaSession* RTSPServer::ServerMediaSessionIterator::next() {
01253   if (fOurIterator == NULL) return NULL;
01254 
01255   char const* key; // dummy
01256   return (ServerMediaSession*)(fOurIterator->next(key));
01257 }
01258 
01259 
01261 
01262 UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm,
01263                                                        Boolean passwordsAreMD5)
01264   : fTable(HashTable::create(STRING_HASH_KEYS)),
01265     fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)),
01266     fPasswordsAreMD5(passwordsAreMD5) {
01267 }
01268 
01269 UserAuthenticationDatabase::~UserAuthenticationDatabase() {
01270   delete[] fRealm;
01271   delete fTable;
01272 }
01273 
01274 void UserAuthenticationDatabase::addUserRecord(char const* username,
01275                                                char const* password) {
01276   fTable->Add(username, (void*)(strDup(password)));
01277 }
01278 
01279 void UserAuthenticationDatabase::removeUserRecord(char const* username) {
01280   char* password = (char*)(fTable->Lookup(username));
01281   fTable->Remove(username);
01282   delete[] password;
01283 }
01284 
01285 char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
01286   return (char const*)(fTable->Lookup(username));
01287 }

Generated on Fri Sep 3 02:35:41 2010 for live by  doxygen 1.5.2