Esempio n. 1
0
//based on libRTMP function
bool rtmp_supplement::WriteN(RTMP * r, const char *buffer, int n)
{
	const char *ptr = buffer;

	while (n > 0) {
#ifdef _DEBUG
		//fwrite(ptr, 1, n, _netstackdump);
#endif

		int nBytes = send(r->m_socket, ptr, n, 0);
		//Log(LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes);

		if (nBytes < 0) {
			int sockerr = GetSockError();
			_log_ptr->Log(LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
			sockerr, n);

			if (sockerr == EINTR && !RTMP_ctrlC)
			continue;

			_rtmp_ptr->RTMP_Close(r);
			n = 1;
			break;
		}

		if (nBytes == 0)
		break;

		n -= nBytes;
		ptr += nBytes;
	}

	return n == 0;
}
Esempio n. 2
0
void
Send( qsrAcceptor * pAcceptor, unsigned int indice )
{
   int errcode = 0;
   char * ptr;
   int offset;
   
   PSO_PRE_CONDITION( pAcceptor != NULL );
   PSO_PRE_CONDITION( indice < FD_SETSIZE );

   ptr = (char*) &pAcceptor->answer;
   offset = sizeof pAcceptor->answer - pAcceptor->dispatch[indice].dataToBeWritten;

   do {
      errcode = send( pAcceptor->dispatch[indice].socketId, 
                      &ptr[offset], 
                      pAcceptor->dispatch[indice].dataToBeWritten, 
                      MSG_NOSIGNAL );
   } while ( errcode == -1 && errno == EINTR );

   if ( errcode == -1 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function send(), error = %d",
                       GetSockError() );
      /*
       * The following attempt at recovering from a send() failure could 
       * well be a bit problematic. We should probably check the exact error
       * that cause the problem and decide which cleanup is needed based
       * on that error... 
       * Since the chance of an error is very small when using the loopback
       * network interface, the work on this will wait!!!
       *
       * \todo Make sure that the code for the cleanup of the sockets (when 
       * in error) is appropriate for the specific error encountered.
       */
#if defined (WIN32)
      shutdown( pAcceptor->dispatch[indice].socketId, SD_BOTH );      
      closesocket( pAcceptor->dispatch[indice].socketId );
#else
      if ( errno != EPIPE ) {
         shutdown( pAcceptor->dispatch[indice].socketId, 2 );      
         close( pAcceptor->dispatch[indice].socketId );
      }
#endif

      pAcceptor->dispatch[indice].socketId = PSO_INVALID_SOCKET;
      qsrSendMessage( &pAcceptor->pQuasar->log,
                       QSR_WARNING, 
                       "Connection terminated abnormally %s%d",
                       "for process ", 
                       pAcceptor->dispatch[indice].pid );      
      return;
   }
   
   pAcceptor->dispatch[indice].dataToBeWritten -= errcode;

   return;
}
Esempio n. 3
0
void
stopStreaming(STREAMING_SERVER * server)
{
  assert(server);

  if (server->state != STREAMING_STOPPED)
    {
      if (server->state == STREAMING_IN_PROGRESS)
	{
	  server->state = STREAMING_STOPPING;

	  // wait for streaming threads to exit
	  while (server->state != STREAMING_STOPPED)
	    msleep(1);
	}

      if (closesocket(server->socket))
	RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
	    GetSockError());

      server->state = STREAMING_STOPPED;
    }
}
Esempio n. 4
0
void processTCPrequest(STREAMING_SERVER * server,	// server socket and state (our listening socket)
		       int sockfd	// client connection socket
  )
{
  char buf[512] = { 0 };	// answer buffer
  char header[2048] = { 0 };	// request header
  char *filename = NULL;	// GET request: file name //512 not enuf
  char *buffer = NULL;		// stream buffer
  char *ptr = NULL;		// header pointer
  int len;

  size_t nRead = 0;
  RTMP rtmp = { 0 };
  uint32_t dSeek = 0;		// can be used to start from a later point in the stream

  // reset RTMP options to defaults specified upon invokation of streams
  RTMP_REQUEST req;
  char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n";
  // timeout for http requests
  fd_set fds;
  struct timeval tv;
  char *status = "404 Not Found";

  server->state = STREAMING_IN_PROGRESS;


  memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST));



  memset(&tv, 0, sizeof(struct timeval));
  tv.tv_sec = 5;

  // go through request lines
  //do {
  FD_ZERO(&fds);
  FD_SET(sockfd, &fds);

  if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
    {
      RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
      goto quit;
    }
  else
    {
      nRead = recv(sockfd, header, 2047, 0);
      header[2047] = '\0';

      RTMP_Log(RTMP_LOGDEBUG, "%s: header: %s", __FUNCTION__, header);

      if (strstr(header, "Range: bytes=") != 0)
	{
	  // TODO check range starts from 0 and asking till the end.
	  RTMP_LogPrintf("%s, Range request not supported\n", __FUNCTION__);
	  len = sprintf(buf, "HTTP/1.0 416 Requested Range Not Satisfiable%s\r\n",
		  srvhead);
	  send(sockfd, buf, len, 0);
	  goto quit;
	}

      if (strncmp(header, "GET", 3) == 0 && nRead > 4)
	{
        	  char *p = filename;
	  filename = header + 4;

	  // filter " HTTP/..." from end of request

	  while (*p != '\0')
	    {
	      if (*p == ' ')
		{
		  *p = '\0';
		  break;
		}
	      p++;
	    }
	}
    }
  //} while(!isHTTPRequestEOF(header, nRead));

  // if we got a filename from the GET method
  if (filename != NULL)
    {
      RTMP_Log(RTMP_LOGDEBUG, "%s: Request header: %s", __FUNCTION__, filename);
      if (filename[0] == '/')
	{			// if its not empty, is it /?
	  ptr = filename + 1;

	  // parse parameters
	  if (*ptr == '?')
	    {
            int len;
	      ptr++;
	      len = strlen(ptr);

	      while (len >= 2)
		{
		  // get position of the next '&'
		  char *temp;
		  char ich = *ptr;
          unsigned int nArgLen;
          char *arg;
		  ptr++;
		  if (*ptr != '=')
		    goto filenotfound;	// long parameters not (yet) supported

		  ptr++;
		  len -= 2;

		  nArgLen = len;
		  if ((temp = strstr(ptr, "&")) != 0)
		    {
		      nArgLen = temp - ptr;
		    }

		  arg = (char *) malloc((nArgLen + 1) * sizeof(char));
		  memcpy(arg, ptr, nArgLen * sizeof(char));
		  arg[nArgLen] = '\0';

		  //RTMP_Log(RTMP_LOGDEBUG, "%s: unescaping parameter: %s", __FUNCTION__, arg);
		  http_unescape(arg);

		  RTMP_Log(RTMP_LOGDEBUG, "%s: parameter: %c, arg: %s", __FUNCTION__,
		      ich, arg);

		  ptr += nArgLen + 1;
		  len -= nArgLen + 1;

		  if (!ParseOption(ich, arg, &req))
		    {
		      status = "400 unknown option";
		      goto filenotfound;
		    }
		}
	    }
	}
      else
	{
	  goto filenotfound;
	}
    }
  else
    {
      RTMP_LogPrintf("%s: No request header received/unsupported method\n",
		__FUNCTION__);
    }

  // do necessary checks right here to make sure the combined request of default values and GET parameters is correct
  if (!req.hostname.av_len)
    {
      RTMP_Log(RTMP_LOGERROR,
	  "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
      status = "400 Missing Hostname";
      goto filenotfound;
    }
  if (req.playpath.av_len == 0)
    {
      RTMP_Log(RTMP_LOGERROR,
	  "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
      status = "400 Missing Playpath";
      goto filenotfound;;
    }

  if (req.protocol == RTMP_PROTOCOL_UNDEFINED)
    {
      RTMP_Log(RTMP_LOGWARNING,
	  "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
      req.protocol = RTMP_PROTOCOL_RTMP;
    }
  if (req.rtmpport == -1)
    {
      RTMP_Log(RTMP_LOGWARNING,
	  "You haven't specified a port (--port) or rtmp url (-r), using default port");
      req.rtmpport = 0;
    }
  if (req.rtmpport == 0)
    {
      if (req.protocol & RTMP_FEATURE_SSL)
	req.rtmpport = 443;
      else if (req.protocol & RTMP_FEATURE_HTTP)
	req.rtmpport = 80;
      else
	req.rtmpport = 1935;
    }

  if (req.tcUrl.av_len == 0)
    {
      char str[512] = { 0 };
      req.tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s",
	RTMPProtocolStringsLower[req.protocol], req.hostname.av_len,
	req.hostname.av_val, req.rtmpport, req.app.av_len, req.app.av_val);
      req.tcUrl.av_val = (char *) malloc(req.tcUrl.av_len + 1);
      strcpy(req.tcUrl.av_val, str);
    }

  if (req.swfVfy)
    {
#ifdef CRYPTO
        if (RTMP_HashSWF(req.swfUrl.av_val, &req.swfSize, req.hash, req.swfAge) == 0)
          {
            req.swfHash.av_val = (char *)req.hash;
            req.swfHash.av_len = RTMP_SWF_HASHLEN;
          }
#endif
    }

  // after validation of the http request send response header
  len = sprintf(buf, "HTTP/1.0 200 OK%sContent-Type: video/flv\r\n\r\n", srvhead);
  send(sockfd, buf, len, 0);

  // send the packets
  buffer = (char *) calloc(PACKET_SIZE, 1);

  // User defined seek offset
  if (req.dStartOffset > 0)
    {
      if (req.bLiveStream)
	RTMP_Log(RTMP_LOGWARNING,
	    "Can't seek in a live stream, ignoring --seek option");
      else
	dSeek += req.dStartOffset;
    }

  if (dSeek != 0)
    {
      RTMP_LogPrintf("Starting at TS: %d ms\n", dSeek);
    }

  RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime);
  RTMP_Init(&rtmp);
  RTMP_SetBufferMS(&rtmp, req.bufferTime);
  RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost,
		   &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, req.dStopOffset,
		   req.bLiveStream, req.timeout);
  /* backward compatibility, we always sent this as true before */
  if (req.auth.av_len)
    rtmp.Link.lFlags |= RTMP_LF_AUTH;

  rtmp.Link.extras = req.extras;
  rtmp.Link.token = req.token;
  rtmp.m_read.timestamp = dSeek;

  RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app);
  if (!RTMP_Connect(&rtmp, NULL))
    {
      RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__);
    }
  else
    {
      unsigned long size = 0;
      double percent = 0;
      double duration = 0.0;

      int nWritten = 0;
      int nRead = 0;

      do
	{
	  nRead = RTMP_Read(&rtmp, buffer, PACKET_SIZE);

	  if (nRead > 0)
	    {
	      if ((nWritten = send(sockfd, buffer, nRead, 0)) < 0)
		{
		  RTMP_Log(RTMP_LOGERROR, "%s, sending failed, error: %d", __FUNCTION__,
		      GetSockError());
		  goto cleanup;	// we are in STREAMING_IN_PROGRESS, so we'll go to STREAMING_ACCEPTING
		}

	      size += nRead;

	      //RTMP_LogPrintf("write %dbytes (%.1f KB)\n", nRead, nRead/1024.0);
	      if (duration <= 0)	// if duration unknown try to get it from the stream (onMetaData)
		duration = RTMP_GetDuration(&rtmp);

	      if (duration > 0)
		{
		  percent =
		    ((double) (dSeek + rtmp.m_read.timestamp)) / (duration *
							   1000.0) * 100.0;
		  percent = ((double) (int) (percent * 10.0)) / 10.0;
		  RTMP_LogStatus("\r%.3f KB / %.2f sec (%.1f%%)",
			    (double) size / 1024.0,
			    (double) (rtmp.m_read.timestamp) / 1000.0, percent);
		}
	      else
		{
		  RTMP_LogStatus("\r%.3f KB / %.2f sec", (double) size / 1024.0,
			    (double) (rtmp.m_read.timestamp) / 1000.0);
		}
	    }
#ifdef _DEBUG
	  else
	    {
	      RTMP_Log(RTMP_LOGDEBUG, "zero read!");
	    }
#endif
	}
      while (server->state == STREAMING_IN_PROGRESS && nRead > -1
	     && RTMP_IsConnected(&rtmp) && nWritten >= 0);
    }
cleanup:
  RTMP_LogPrintf("Closing connection... ");
  RTMP_Close(&rtmp);
  RTMP_LogPrintf("done!\n\n");

quit:
  if (buffer)
    {
      free(buffer);
      buffer = NULL;
    }

  if (sockfd)
    closesocket(sockfd);

  if (server->state == STREAMING_IN_PROGRESS)
    server->state = STREAMING_ACCEPTING;

  return;

filenotfound:
  RTMP_LogPrintf("%s, %s, %s\n", __FUNCTION__, status, filename);
  len = sprintf(buf, "HTTP/1.0 %s%s\r\n", status, srvhead);
  send(sockfd, buf, len, 0);
  goto quit;
}
Esempio n. 5
0
bool Accept( qsrAcceptor * pAcceptor )
{
   PSO_SOCKET newSock = PSO_INVALID_SOCKET;
   int errcode, i;
#if defined (WIN32)
   unsigned long mode = 1;
#endif

#if ! defined (WIN32) 
   do {
#endif
      newSock = accept( pAcceptor->socketFD, NULL, 0 );
#if ! defined (WIN32)
   } while ( newSock == PSO_INVALID_SOCKET && errno == EINTR );
#endif
  
   if ( newSock == PSO_INVALID_SOCKET ) {
      errcode = GetSockError();
#if defined (WIN32) 
      if ( errcode == WSAEWOULDBLOCK )
         return true;
#else
      if ( errcode == EWOULDBLOCK || errcode == EAGAIN )
         return true;
#endif
      // The error is more serious...
      qsrSendMessage( &pAcceptor->pQuasar->log,
                       QSR_ERROR, 
                       "In function accept(), error = %d",
                       GetSockError() );
      return false;
   }
   
   /*
    * Set the new socket in non-blocking mode
    */

#if defined (WIN32)
   errcode = ioctlsocket( newSock, FIONBIO, &mode );
   if ( errcode == SOCKET_ERROR ) {
      qsrSendMessage( &pAcceptor->pQuasar->log,
                       QSR_ERROR, 
                       "In function ioctlsocket(), error = %d",
                       GetSockError() );
      return false;
   }
#else
   errcode = fcntl( newSock, F_SETFL, O_NONBLOCK);
   if ( errcode < 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function fcntl(), error = %d",
                       GetSockError() );
      return false;
   }
#endif

   for ( i = 1; i < FD_SETSIZE; ++i ) {
      if (  pAcceptor->dispatch[i].socketId == PSO_INVALID_SOCKET ) {
         pAcceptor->dispatch[i].socketId = newSock;
         break;
      }
   }
   
   return true;
}
Esempio n. 6
0
void 
qsrWaitForConnections( qsrAcceptor * pAcceptor )
{
   int errcode = 0;
   fd_set readSet, writeSet;
   int maxFD, fired;
   struct timeval timeout;   
   unsigned int i;
   bool rc;
   
   PSO_PRE_CONDITION( pAcceptor != NULL );

   /*
    * NOTE: since socket handles, on Windows, are not integers, we will
    * not used the handles as indices to the pAcceptor->dispatch array
    */

   pAcceptor->dispatch[0].socketId = pAcceptor->socketFD;
   pAcceptor->dispatch[0].pid = 0;
   pAcceptor->dispatch[0].dataToBeWritten = false;
   
   for ( i = 1; i < FD_SETSIZE; ++i ) {
      pAcceptor->dispatch[i].socketId = PSO_INVALID_SOCKET;
      pAcceptor->dispatch[i].pid = -1;
      pAcceptor->dispatch[i].dataToBeWritten = false;
   }   

   while ( true ) {
      int zzz = 0;
      if ( g_pQSR->controlWord & QSR_SHUTDOWN_REQUEST ) {
         break;
      }
      
      timeout.tv_sec = 1;
      timeout.tv_usec = 0;
      FD_ZERO( &readSet  );
      FD_ZERO( &writeSet );
      maxFD = 0;
      for ( i = 0; i < FD_SETSIZE; ++i ) {
         
         if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET) {
            if ( pAcceptor->dispatch[i].dataToBeWritten == 0 ) {
               FD_SET( pAcceptor->dispatch[i].socketId, &readSet);
               zzz++;
            }
            else {
               FD_SET( pAcceptor->dispatch[i].socketId, &writeSet);
               zzz++;
            }
#if ! defined (WIN32)
            if ( pAcceptor->dispatch[i].socketId+1 > maxFD ) {
               maxFD = pAcceptor->dispatch[i].socketId+1;
            }
#endif
         }
      }

      do {
         fired = select( maxFD, &readSet, &writeSet, NULL, &timeout );
      } while ( fired == -1 && errno == EINTR );
      

      if ( fired == -1 ) {
         qsrSendMessage( &pAcceptor->pQuasar->log, 
                          QSR_ERROR, 
                          "In function select(), error = %d",
                          GetSockError() );
         errcode = -1;
         break;
      }
      if ( fired == 0 ) continue;
      
      /*
       * Start with the socket listening for new connection requests
       */
      if ( FD_ISSET( pAcceptor->socketFD, &readSet ) ) {
         rc = Accept( pAcceptor );
         PSO_POST_CONDITION( rc == true || rc == false );
         if ( ! rc ) break;
         fired--;
      }
      if ( fired == 0 ) continue;
      
      /*
       * Process all open sockets 
       */
      for ( i = 1; i < FD_SETSIZE; ++i ) {
         if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET ) {
            if ( FD_ISSET( pAcceptor->dispatch[i].socketId, &writeSet ) ) {
               Send( pAcceptor, i );
               fired--;
            }
            else if ( FD_ISSET( pAcceptor->dispatch[i].socketId, &readSet ) ) {
               Receive( pAcceptor, i );
               fired--;
            }
         }
         if ( fired == 0 ) break;
      }
   }

   // Cleanup (close all sockets)
   for ( i = 0; i < FD_SETSIZE; ++i ) {
      if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET ) {
#if defined (WIN32) 
         shutdown( pAcceptor->dispatch[i].socketId, SD_BOTH );      
         closesocket( pAcceptor->dispatch[i].socketId );
#else
         shutdown( pAcceptor->dispatch[i].socketId, 2 );      
         close( pAcceptor->dispatch[i].socketId );
#endif
      }
   }
   pAcceptor->socketFD = PSO_INVALID_SOCKET;

}
Esempio n. 7
0
bool qsrPrepareConnection( qsrAcceptor * pAcceptor,
                           qsrQuasar   * pQuasar )
{
   int errcode = 0;
   int one = 1;
   unsigned short port;
   long int dummy = 0;
#if defined (WIN32) 
   WORD versionRequested;
   WSADATA wsaData;
   unsigned long mode = 1;
#endif
   struct sockaddr_in addr;

   PSO_PRE_CONDITION( pAcceptor != NULL );
   PSO_PRE_CONDITION( pQuasar   != NULL );

   pAcceptor->pQuasar = pQuasar;
   memset( &pAcceptor->answer, 0, sizeof pAcceptor->answer );
   strcpy( pAcceptor->answer.pathname, pAcceptor->pQuasar->params.memLocation );
   pAcceptor->answer.memorySizekb = pAcceptor->pQuasar->params.memorySizekb;

   dummy = strtol( pAcceptor->pQuasar->params.qsrAddress, NULL, 10 );
   if ( dummy <= 0 || dummy > 65535 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "Error getting port number" );
      return false;
   }
   port = (unsigned short) dummy;

#if defined (WIN32) 
   versionRequested = MAKEWORD( 2, 2 );
 
   errcode = WSAStartup( versionRequested, &wsaData );
   if ( errcode != 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function WSAStartup(), error = %d",
                       errcode );
      return false;
   }
   pAcceptor->cleanupNeeded = true;
   
#endif

   pAcceptor->socketFD = socket( PF_INET, SOCK_STREAM, 0 );
   if ( pAcceptor->socketFD == PSO_INVALID_SOCKET ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function socket(), error = %d",
                       GetSockError() );
      return false;
   }
   
   errcode = setsockopt( pAcceptor->socketFD, SOL_SOCKET, SO_REUSEADDR, 
                         (const char *)&one, sizeof (one) );
   if ( errcode != 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function setsockopt(), error = %d",
                       GetSockError() );
      return false;
   }

   // Set the socket in non-blocking mode.

#if defined (WIN32)
   errcode = ioctlsocket( pAcceptor->socketFD, FIONBIO, &mode );
   if ( errcode == SOCKET_ERROR ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function ioctlsocket(), error = %d",
                       GetSockError() );
      return false;
   }
#else
   errcode = fcntl( pAcceptor->socketFD, F_SETFL, O_NONBLOCK);
   if ( errcode < 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function fcntl(), error = %d",
                       GetSockError() );
      return false;
   }
#endif

   memset( &addr, 0, sizeof(struct sockaddr_in) );

   addr.sin_family = PF_INET;
   addr.sin_port = htons( port );
   addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );

   errcode = bind( pAcceptor->socketFD, 
                   (struct sockaddr *) &addr,
                   sizeof(struct sockaddr_in) );
   if ( errcode != 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function bind(), error = %d",
                       GetSockError() );
      return false;
   }

   errcode = listen( pAcceptor->socketFD, 5 );
   if ( errcode != 0 ) {
      qsrSendMessage( &pAcceptor->pQuasar->log, 
                       QSR_ERROR, 
                       "In function listen(), error = %d",
                       GetSockError() );
      return false;
   }
   
   return true;
}