Esempio n. 1
0
void RTSPServer::RTSPClientSession
::handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
				 char const* fullRequestStr) {
  char* sdpDescription = NULL;
  char* rtspURL = NULL;
  do {
		  if (!authenticationOK("DESCRIBE", cseq, urlSuffix, fullRequestStr))
				  break;

		// We should really check that the request contains an "Accept:" #####
		// for "application/sdp", because that's what we're sending back #####

		// Begin by looking up the "ServerMediaSession" object for the
		// specified "urlSuffix":
	  ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
	  if (session == NULL) {
		  handleCmd_notFound(cseq);
		  break;
		}

		// Then, assemble a SDP description for this session:
	  sdpDescription = session->generateSDPDescription();
	  if (sdpDescription == NULL) {
			// This usually means that a file name that was specified for a
			// "ServerMediaSubsession" does not exist.
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
				 "CSeq: %s\r\n"
				 "%s\r\n",
				 cseq,
				 dateHeader());
		 break;
		}
	  unsigned sdpDescriptionSize = strlen(sdpDescription);

		// Also, generate our RTSP URL, for the "Content-Base:" header
		// (which is necessary to ensure that the correct URL gets used in
		// subsequent "SETUP" requests).
	  rtspURL = fOurServer.rtspURL(session, fClientSocket);

	  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
			 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
			 "%s"
			 "Content-Base: %s/\r\n"
			 "Content-Type: application/sdp\r\n"
			 "Content-Length: %d\r\n\r\n"
			 "%s",
			 cseq,
			 dateHeader(),
			 rtspURL,
			 sdpDescriptionSize,
			 sdpDescription);
	} while (0);

  delete[] sdpDescription;
  delete[] rtspURL;
}
Esempio n. 2
0
void RTSPServer::RTSPClientSession
::handleCmd_TEARDOWN(ServerMediaSubsession* /*subsession*/, char const* cseq) {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%s\r\n",
		 cseq, dateHeader());
  fSessionIsActive = False; // triggers deletion of ourself after responding
}
Esempio n. 3
0
void RTSPServer::RTSPClientSession
::handleCmd_GET_PARAMETER(ServerMediaSubsession* subsession, char const* cseq,
			  char const* /*fullRequestStr*/) {
	// We implement "GET_PARAMETER" just as a 'keep alive',
	// and send back an empty response:
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
		 cseq, dateHeader(), fOurSessionId);
}
Esempio n. 4
0
void RTSPServer::RTSPClientSession
	::handleCmd_PAUSE(ServerMediaSubsession* subsession, char const* cseq) {
  for (unsigned i = 0; i < fNumStreamStates; ++i) {
	  if (subsession == NULL /* means: aggregated operation */
	|| subsession == fStreamStates[i].subsession) {
		  fStreamStates[i].subsession->pauseStream(fOurSessionId,
								 fStreamStates[i].streamToken);
		}
	}
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %08X\r\n\r\n",
		 cseq, dateHeader(), fOurSessionId);
}
void RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStreaming
::handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* /*fullRequestStr*/) {
  // If "urlSuffix" ends with "?segment=<offset-in-seconds>,<duration-in-seconds>", then strip this off, and send the
  // specified segment.  Otherwise, construct and send a playlist that consists of segments from the specified file.
  do {
    char const* questionMarkPos = strrchr(urlSuffix, '?');
    if (questionMarkPos == NULL) break;
    unsigned offsetInSeconds, durationInSeconds;
    if (sscanf(questionMarkPos, "?segment=%u,%u", &offsetInSeconds, &durationInSeconds) != 2) break;

    char* streamName = strDup(urlSuffix);
    streamName[questionMarkPos-urlSuffix] = '\0';

    do {
      ServerMediaSession* session = fOurServer.lookupServerMediaSession(streamName);
      if (session == NULL) {
	handleHTTPCmd_notFound();
	break;
      }

      // We can't send multi-subsession streams over HTTP (because there's no defined way to multiplex more than one subsession).
      // Therefore, use the first (and presumed only) substream:
      ServerMediaSubsessionIterator iter(*session);
      ServerMediaSubsession* subsession = iter.next();
      if (subsession == NULL) {
	// Treat an 'empty' ServerMediaSession the same as one that doesn't exist at all:
	handleHTTPCmd_notFound();
	break;
      }

      // Call "getStreamParameters()" to create the stream's source.  (Because we're not actually streaming via RTP/RTCP, most
      // of the parameters to the call are dummy.)
      ++fClientSessionId;
      Port clientRTPPort(0), clientRTCPPort(0), serverRTPPort(0), serverRTCPPort(0);
      netAddressBits destinationAddress = 0;
      u_int8_t destinationTTL = 0;
      Boolean isMulticast = False;
      void* streamToken;
      subsession->getStreamParameters(fClientSessionId, 0, clientRTPPort,clientRTCPPort, -1,0,0, destinationAddress,destinationTTL, isMulticast, serverRTPPort,serverRTCPPort, streamToken);
      
      // Seek the stream source to the desired place, with the desired duration, and (as a side effect) get the number of bytes:
      double dOffsetInSeconds = (double)offsetInSeconds;
      u_int64_t numBytes;
      subsession->seekStream(fClientSessionId, streamToken, dOffsetInSeconds, (double)durationInSeconds, numBytes);
      unsigned numTSBytesToStream = (unsigned)numBytes;
      
      if (numTSBytesToStream == 0) {
	// For some reason, we do not know the size of the requested range.  We can't handle this request:
	handleHTTPCmd_notSupported();
	break;
      }
      
      // Construct our response:
      snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
	       "HTTP/1.1 200 OK\r\n"
	       "%s"
	       "Server: LIVE555 Streaming Media v%s\r\n"
	       "%s"
	       "Content-Length: %d\r\n"
	       "Content-Type: text/plain; charset=ISO-8859-1\r\n"
	       "\r\n",
	       dateHeader(),
	       LIVEMEDIA_LIBRARY_VERSION_STRING,
	       lastModifiedHeader(streamName),
	       numTSBytesToStream);
      // Send the response now, because we're about to add more data (from the source):
      send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
      fResponseBuffer[0] = '\0'; // We've already sent the response.  This tells the calling code not to send it again.
      
      // Ask the media source to deliver - to the TCP sink - the desired data:
      if (fStreamSource != NULL) { // sanity check
	if (fTCPSink != NULL) fTCPSink->stopPlaying();
	Medium::close(fStreamSource);
      }
      fStreamSource = subsession->getStreamSource(streamToken);
      if (fStreamSource != NULL) {
	if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
	fTCPSink->startPlaying(*fStreamSource, afterStreaming, this);
      }
    } while(0);

    delete[] streamName;
    return;
  } while (0);

  // "urlSuffix" does not end with "?segment=<offset-in-seconds>,<duration-in-seconds>".
  // Construct and send a playlist that describes segments from the specified file.

  // First, make sure that the named file exists, and is streamable:
  ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
  if (session == NULL) {
    handleHTTPCmd_notFound();
    return;
  }

  // To be able to construct a playlist for the requested file, we need to know its duration:
  float duration = session->duration();
  if (duration <= 0.0) {
    // We can't handle this request:
    handleHTTPCmd_notSupported();
    return;
  }

  // Now, construct the playlist.  It will consist of a prefix, one or more media file specifications, and a suffix:
  unsigned const maxIntLen = 10; // >= the maximum possible strlen() of an integer in the playlist
  char const* const playlistPrefixFmt =
    "#EXTM3U\r\n"
    "#EXT-X-ALLOW-CACHE:YES\r\n"
    "#EXT-X-MEDIA-SEQUENCE:0\r\n"
    "#EXT-X-TARGETDURATION:%d\r\n";
  unsigned const playlistPrefixFmt_maxLen = strlen(playlistPrefixFmt) + maxIntLen;

  char const* const playlistMediaFileSpecFmt =
    "#EXTINF:%d,\r\n"
    "%s?segment=%d,%d\r\n";
  unsigned const playlistMediaFileSpecFmt_maxLen = strlen(playlistMediaFileSpecFmt) + maxIntLen + strlen(urlSuffix) + 2*maxIntLen;

  char const* const playlistSuffixFmt =
    "#EXT-X-ENDLIST\r\n";
  unsigned const playlistSuffixFmt_maxLen = strlen(playlistSuffixFmt);

  // Figure out the 'target duration' that will produce a playlist that will fit in our response buffer.  (But make it at least 10s.)
  unsigned const playlistMaxSize = 10000;
  unsigned const mediaFileSpecsMaxSize = playlistMaxSize - (playlistPrefixFmt_maxLen + playlistSuffixFmt_maxLen);
  unsigned const maxNumMediaFileSpecs = mediaFileSpecsMaxSize/playlistMediaFileSpecFmt_maxLen;

  unsigned targetDuration = (unsigned)(duration/maxNumMediaFileSpecs + 1);
  if (targetDuration < 10) targetDuration = 10;

  char* playlist = new char[playlistMaxSize];
  char* s = playlist;
  sprintf(s, playlistPrefixFmt, targetDuration);
  s += strlen(s);

  unsigned durSoFar = 0;
  while (1) {
    unsigned dur = targetDuration < duration ? targetDuration : (unsigned)duration;
    duration -= dur;
    sprintf(s, playlistMediaFileSpecFmt, dur, urlSuffix, durSoFar, dur);
    s += strlen(s);
    if (duration < 1.0) break;

    durSoFar += dur;
  }

  sprintf(s, playlistSuffixFmt);
  s += strlen(s);
  unsigned playlistLen = s - playlist;

  // Construct our response:
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
	   "HTTP/1.1 200 OK\r\n"
	   "%s"
	   "Server: LIVE555 Streaming Media v%s\r\n"
	   "%s"
	   "Content-Length: %d\r\n"
	   "Content-Type: application/vnd.apple.mpegurl\r\n"
	   "\r\n",
	   dateHeader(),
	   LIVEMEDIA_LIBRARY_VERSION_STRING,
	   lastModifiedHeader(urlSuffix),
	   playlistLen);

  // Send the response header now, because we're about to add more data (the playlist):
  send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
  fResponseBuffer[0] = '\0'; // We've already sent the response.  This tells the calling code not to send it again.

  // Then, send the playlist.  Because it's large, we don't do so using "send()", because that might not send it all at once.
  // Instead, we stream the playlist over the TCP socket:
  if (fPlaylistSource != NULL) { // sanity check
    if (fTCPSink != NULL) fTCPSink->stopPlaying();
    Medium::close(fPlaylistSource);
  }
  fPlaylistSource = ByteStreamMemoryBufferSource::createNew(envir(), (u_int8_t*)playlist, playlistLen);
  if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
  fTCPSink->startPlaying(*fPlaylistSource, afterStreaming, this);
}
Esempio n. 6
0
void RTSPServer::RTSPClientSession
	::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,
			 char const* fullRequestStr) {
  char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientSocket);
  unsigned rtspURLSize = strlen(rtspURL);

	// Parse the client's "Scale:" header, if any:
  float scale;
  Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);

	// Try to set the stream's scale factor to this value:
  if (subsession == NULL /*aggregate op*/) {
	  fOurServerMediaSession->testScaleFactor(scale);
	} else {
	  subsession->testScaleFactor(scale);
	}

  char buf[100];
  char* scaleHeader;
  if (!sawScaleHeader) {
	  buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
	} else {
	  sprintf(buf, "Scale: %f\r\n", scale);
	}
  scaleHeader = strDup(buf);

	// Parse the client's "Range:" header, if any:
  double rangeStart = 0.0, rangeEnd = 0.0;
  Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd);

	// Use this information, plus the stream's duration (if known), to create
	// our own "Range:" header, for the response:
  float duration = subsession == NULL /*aggregate op*/
		? fOurServerMediaSession->duration() : subsession->duration();
  if (duration < 0.0) {
		// We're an aggregate PLAY, but the subsessions have different durations.
		// Use the largest of these durations in our header
	  duration = -duration;
	}

  if (rangeEnd <= 0.0 || rangeEnd > duration) rangeEnd = duration;
  if (rangeStart < 0.0) {
	  rangeStart = 0.0;
	} else if (rangeEnd > 0.0 && scale > 0.0 && rangeStart > rangeEnd) {
	  rangeStart = rangeEnd;
	}

  char* rangeHeader;
  if (!sawRangeHeader) {
	  buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back
	} else if (rangeEnd == 0.0 && scale >= 0.0) {
	  sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
	} else {
	  sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
	}
  rangeHeader = strDup(buf);

	// Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
  char const* rtpInfoFmt =
		"%s" // "RTP-Info:", plus any preceding rtpInfo items
		"%s" // comma separator, if needed
		"url=%s/%s"
		";seq=%d"
		";rtptime=%u"
		;
  unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
  char* rtpInfo = strDup("RTP-Info: ");
  unsigned i, numRTPInfoItems = 0;

	// Do any required seeking/scaling on each subsession, before starting streaming:
  for (i = 0; i < fNumStreamStates; ++i) {
	  if (subsession == NULL /* means: aggregated operation */
	|| subsession == fStreamStates[i].subsession) {
		  if (sawScaleHeader) {
	fStreamStates[i].subsession->setStreamScale(fOurSessionId,
							  fStreamStates[i].streamToken,
							  scale);
			}
		  if (sawRangeHeader) {
	fStreamStates[i].subsession->seekStream(fOurSessionId,
						fStreamStates[i].streamToken,
						rangeStart);
			}
		}
	}

	// Now, start streaming:
  for (i = 0; i < fNumStreamStates; ++i) {
	  if (subsession == NULL /* means: aggregated operation */
	|| subsession == fStreamStates[i].subsession) {
		  unsigned short rtpSeqNum = 0;
		  unsigned rtpTimestamp = 0;
		  fStreamStates[i].subsession->startStream(fOurSessionId,
								 fStreamStates[i].streamToken,
								 (TaskFunc*)noteClientLiveness,
								 this,			//save client session pointer for livenessTimeoutTask
								 rtpSeqNum,
								 rtpTimestamp);
		  const char *urlSuffix = fStreamStates[i].subsession->trackId();
		  char* prevRTPInfo = rtpInfo;
		  unsigned rtpInfoSize = rtpInfoFmtSize
	+ strlen(prevRTPInfo)
	+ 1
	+ rtspURLSize + strlen(urlSuffix)
	+ 5 /*max unsigned short len*/
	+ 10 /*max unsigned (32-bit) len*/
	+ 2 /*allows for trailing \r\n at final end of string*/;
		  rtpInfo = new char[rtpInfoSize];
		  sprintf(rtpInfo, rtpInfoFmt,
			  prevRTPInfo,
			  numRTPInfoItems++ == 0 ? "" : ",",
			  rtspURL, urlSuffix,
			  rtpSeqNum,
			  rtpTimestamp
				);
		  delete[] prevRTPInfo;
		}
	}
  if (numRTPInfoItems == 0) {
	  rtpInfo[0] = '\0';
	} else {
	  unsigned rtpInfoLen = strlen(rtpInfo);
	  rtpInfo[rtpInfoLen] = '\r';
	  rtpInfo[rtpInfoLen+1] = '\n';
	  rtpInfo[rtpInfoLen+2] = '\0';
	}

	// Fill in the response:
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 200 OK\r\n"
		 "CSeq: %s\r\n"
		 "%s"
		 "%s"
		 "%s"
		 "Session: %08X\r\n"
		 "%s\r\n",
		 cseq,
		 dateHeader(),
		 scaleHeader,
		 rangeHeader,
		 fOurSessionId,
		 rtpInfo);
  delete[] rtpInfo; delete[] rangeHeader;
  delete[] scaleHeader; delete[] rtspURL;
}
Esempio n. 7
0
void RTSPServer::RTSPClientSession
::handleCmd_SETUP(char const* cseq,
		  char const* urlPreSuffix, char const* urlSuffix,
		  char const* fullRequestStr) {
	// "urlPreSuffix" should be the session (stream) name, and
	// "urlSuffix" should be the subsession (track) name.
  char const* streamName = urlPreSuffix;
  char const* trackId = urlSuffix;

	// Check whether we have existing session state, and, if so, whether it's
	// for the session that's named in "streamName".	(Note that we don't
	// support more than one concurrent session on the same client connection.) #####
  if (fOurServerMediaSession != NULL
			&& strcmp(streamName, fOurServerMediaSession->streamName()) != 0) {
	  fOurServerMediaSession = NULL;
	}
  if (fOurServerMediaSession == NULL) {
		// Set up this session's state.

		// Look up the "ServerMediaSession" object for the specified stream:
	  if (streamName[0] != '\0' ||
	fOurServer.lookupServerMediaSession("") != NULL) { // normal case
		} else { // weird case: there was no track id in the URL
		  streamName = urlSuffix;
		  trackId = NULL;
		}
	  fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
	  if (fOurServerMediaSession == NULL) {
		  handleCmd_notFound(cseq);
		  return;
		}

	  fOurServerMediaSession->incrementReferenceCount();

		// Set up our array of states for this session's subsessions (tracks):
	  reclaimStreamStates();
	  ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
	  for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {}
	  fStreamStates = new struct streamState[fNumStreamStates];
	  iter.reset();
	  ServerMediaSubsession* subsession;
	  for (unsigned i = 0; i < fNumStreamStates; ++i) {
		  subsession = iter.next();
		  fStreamStates[i].subsession = subsession;
		  fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later
		}
	}

	// Look up information for the specified subsession (track):
  ServerMediaSubsession* subsession = NULL;
  unsigned streamNum;
  if (trackId != NULL && trackId[0] != '\0') { // normal case
	  for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
		  subsession = fStreamStates[streamNum].subsession;
		  if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
		}
	  if (streamNum >= fNumStreamStates) {
			// The specified track id doesn't exist, so this request fails:
		  handleCmd_notFound(cseq);
		  return;
		}
	} else {
		// Weird case: there was no track id in the URL.
		// This works only if we have only one subsession:
	  if (fNumStreamStates != 1) {
		  handleCmd_bad(cseq);
		  return;
		}
	  streamNum = 0;
	  subsession = fStreamStates[streamNum].subsession;
	}
	// ASSERT: subsession != NULL

	// Look for a "Transport:" header in the request string,
	// to extract client parameters:
  StreamingMode streamingMode;
  char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
  char* clientsDestinationAddressStr;
  u_int8_t clientsDestinationTTL;
  portNumBits clientRTPPortNum, clientRTCPPortNum;
  unsigned char rtpChannelId, rtcpChannelId;
  parseTransportHeader(fullRequestStr, streamingMode, streamingModeString, fIsMulticast,
					 clientsDestinationAddressStr, clientsDestinationTTL,
					 clientRTPPortNum, clientRTCPPortNum,
					 rtpChannelId, rtcpChannelId);
  if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) {
		// TCP streaming was requested, but with no "interleaving=" fields.
		// (QuickTime Player sometimes does this.)  Set the RTP and RTCP channel ids to
		// proper values:
	  rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
	}
  fTCPStreamIdCount += 2;

  Port clientRTPPort(clientRTPPortNum);
  Port clientRTCPPort(clientRTCPPortNum);

	// Next, check whether a "Range:" header is present in the request.
	// This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
  double rangeStart = 0.0, rangeEnd = 0.0;
  fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) ||
										  parsePlayNowHeader(fullRequestStr);

	// Then, get server parameters from the 'subsession':
  int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1;
  netAddressBits destinationAddress = 0;
  u_int8_t destinationTTL = 5;
#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
  if (clientsDestinationAddressStr != NULL) {
		// Use the client-provided "destination" address.
		// Note: This potentially allows the server to be used in denial-of-service
		// attacks, so don't enable this code unless you're sure that clients are
		// trusted.
	  destinationAddress = our_inet_addr(clientsDestinationAddressStr);
	}
	// Also use the client-provided TTL.
  destinationTTL = clientsDestinationTTL;
#endif
  delete[] clientsDestinationAddressStr;
  Port serverRTPPort(0);
  Port serverRTCPPort(0);
  subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
				  clientRTPPort, clientRTCPPort,
				  tcpSocketNum, rtpChannelId, rtcpChannelId,
				  destinationAddress, destinationTTL, fIsMulticast,
				  serverRTPPort, serverRTCPPort,
				  fStreamStates[streamNum].streamToken);
  struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
  char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));
  struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
  getsockname(fClientSocket, (struct sockaddr*)&sourceAddr, &namelen);
  char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));
  if (fIsMulticast) {
	  switch (streamingMode) {
	  case RTP_UDP:
			  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
								 "RTSP/1.0 200 OK\r\n"
								 "CSeq: %s\r\n"
								 "%s"
								 "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
								 "Session: %08X\r\n\r\n",
								 cseq,
								 dateHeader(),
								 destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
								 fOurSessionId);
			  break;
	  case RTP_TCP:
				// multicast streams can't be sent via TCP
			  handleCmd_unsupportedTransport(cseq);
			  break;
	  case RAW_UDP:
			  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
								 "RTSP/1.0 200 OK\r\n"
								 "CSeq: %s\r\n"
								 "%s"
								 "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
								 "Session: %08X\r\n\r\n",
								 cseq,
								 dateHeader(),
								 streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL,
								 fOurSessionId);
			  break;
		}
	} else {
	  switch (streamingMode) {
	  case RTP_UDP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
				 fOurSessionId);
		  break;
		}
	  case RTP_TCP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,
				 fOurSessionId);
		  break;
		}
	  case RAW_UDP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
				 fOurSessionId);
		  break;
		}
		}
	}
  delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString;
}
Esempio n. 8
0
void RTSPServer::RTSPClientSession::handleCmd_OPTIONS(char const* cseq) {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
		 cseq, dateHeader(), allowedCommandNames);
}
Esempio n. 9
0
void RTSPServer::RTSPClientSession::handleCmd_unsupportedTransport(char const* cseq) {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 461 Unsupported Transport\r\nCSeq: %s\r\n%s\r\n",
		 cseq, dateHeader());
  fSessionIsActive = False; // triggers deletion of ourself after responding
}
Esempio n. 10
0
void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
		 cseq, dateHeader());
  fSessionIsActive = False; // triggers deletion of ourself after responding
}
Esempio n. 11
0
void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
		 cseq, dateHeader(), allowedCommandNames);
}
Esempio n. 12
0
void RTSPServer::RTSPClientSession::handleCmd_bad(char const* /*cseq*/) {
	// Don't do anything with "cseq", because it might be nonsense
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
		 dateHeader(), allowedCommandNames);
}
Esempio n. 13
0
Boolean RTSPServer::RTSPClientSession
::authenticationOK(char const* cmdName, char const* cseq,
			 char const* urlSuffix, char const* fullRequestStr) {

  if (!fOurServer.specialClientAccessCheck(fClientSocket, fClientAddr, urlSuffix)) {
	  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
						 "RTSP/1.0 401 Unauthorized\r\n"
						 "CSeq: %s\r\n"
						 "%s"
						 "\r\n",
						 cseq, dateHeader());
	  return False;
	}

	// If we weren't set up with an authentication database, we're OK:
  if (fOurServer.fAuthDB == NULL) return True;

  char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
  char const* uri = NULL; char const* response = NULL;
  Boolean success = False;

  do {
		// To authenticate, we first need to have a nonce set up
		// from a previous attempt:
	  if (fCurrentAuthenticator.nonce() == NULL) break;

		// Next, the request needs to contain an "Authorization:" header,
		// containing a username, (our) realm, (our) nonce, uri,
		// and response string:
	  if (!parseAuthorizationHeader(fullRequestStr,
				  username, realm, nonce, uri, response)
	|| username == NULL
	|| realm == NULL || strcmp(realm, fCurrentAuthenticator.realm()) != 0
	|| nonce == NULL || strcmp(nonce, fCurrentAuthenticator.nonce()) != 0
	|| uri == NULL || response == NULL) {
		  break;
		}

		// Next, the username has to be known to us:
	  char const* password = fOurServer.fAuthDB->lookupPassword(username);
#ifdef DEBUG
	  fprintf(stderr, "lookupPassword(%s) returned password %s\n", username, password);
#endif
	  if (password == NULL) break;
	  fCurrentAuthenticator.
		  setUsernameAndPassword(username, password,
					 fOurServer.fAuthDB->passwordsAreMD5());

		// Finally, compute a digest response from the information that we have,
		// and compare it to the one that we were given:
	  char const* ourResponse
			= fCurrentAuthenticator.computeDigestResponse(cmdName, uri);
	  success = (strcmp(ourResponse, response) == 0);
	  fCurrentAuthenticator.reclaimDigestResponse(ourResponse);
	} while (0);

  delete[] (char*)username; delete[] (char*)realm; delete[] (char*)nonce;
  delete[] (char*)uri; delete[] (char*)response;
  if (success) return True;

	// If we get here, there was some kind of authentication failure.
	// Send back a "401 Unauthorized" response, with a new random nonce:
  fCurrentAuthenticator.setRealmAndRandomNonce(fOurServer.fAuthDB->realm());
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
		 "RTSP/1.0 401 Unauthorized\r\n"
		 "CSeq: %s\r\n"
		 "%s"
		 "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n\r\n",
		 cseq,
		 dateHeader(),
		 fCurrentAuthenticator.realm(), fCurrentAuthenticator.nonce());
  return False;
}