|
MythTV
0.26-pre
|
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 }
1.7.6.1