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