]> git.sesse.net Git - vlc/blobdiff - modules/stream_out/rtsp.c
Implement dummy RTSP GET_PARAMETER so that clients can "ping"
[vlc] / modules / stream_out / rtsp.c
index 3689113f4ad5c69459f1b1c8465c4fc62c7411df..0979fffcf038ef5154f59fb16d1c225c43fb52da 100644 (file)
@@ -1,7 +1,9 @@
 /*****************************************************************************
  * rtsp.c: RTSP support for RTP stream output module
  *****************************************************************************
- * Copyright (C) 2003-2007 the VideoLAN team
+ * Copyright (C) 2003-2004 the VideoLAN team
+ * Copyright © 2007 Rémi Denis-Courmont
+ *
  * $Id: rtp.c 21407 2007-08-22 20:10:41Z courmisch $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
@@ -31,6 +33,8 @@
 #include <vlc_url.h>
 #include <vlc_network.h>
 #include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
 
 #include "rtp.h"
 
@@ -42,8 +46,8 @@ struct rtsp_stream_t
     sout_stream_t  *owner;
     httpd_host_t   *host;
     httpd_url_t    *url;
-    char           *psz_control;
     char           *psz_path;
+    unsigned        port;
 
     int             sessionc;
     rtsp_session_t **sessionv;
@@ -51,18 +55,18 @@ struct rtsp_stream_t
 
 
 static int  RtspCallback( httpd_callback_sys_t *p_args,
-                          httpd_client_t *cl,
-                          httpd_message_t *answer, httpd_message_t *query );
+                          httpd_client_t *cl, httpd_message_t *answer,
+                          const httpd_message_t *query );
 static int  RtspCallbackId( httpd_callback_sys_t *p_args,
-                            httpd_client_t *cl,
-                            httpd_message_t *answer, httpd_message_t *query );
+                            httpd_client_t *cl, httpd_message_t *answer,
+                            const httpd_message_t *query );
 static void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session );
 
 rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
 {
     rtsp_stream_t *rtsp = malloc( sizeof( *rtsp ) );
 
-    if( rtsp == NULL )
+    if( rtsp == NULL || ( url->i_port > 99999 ) )
         return NULL;
 
     rtsp->owner = p_stream;
@@ -73,10 +77,13 @@ rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
     msg_Dbg( p_stream, "rtsp setup: %s : %d / %s\n",
              url->psz_host, url->i_port, url->psz_path );
 
-    rtsp->psz_path = strdup( url->psz_path ? url->psz_path : "/" );
-    if( rtsp->psz_path == NULL )
-        goto error;
+    rtsp->port = (url->i_port > 0) ? url->i_port : 554;
+    if( url->psz_path != NULL )
+        rtsp->psz_path = strdup( url->psz_path + 1 );
+    else
+        rtsp->psz_path = NULL;
 
+#if 0
     if( asprintf( &rtsp->psz_control, "rtsp://%s:%d%s",
                   url->psz_host,  url->i_port > 0 ? url->i_port : 554,
                   rtsp->psz_path ) == -1 )
@@ -84,14 +91,16 @@ rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
         rtsp->psz_control = NULL;
         goto error;
     }
+#endif
 
     rtsp->host = httpd_HostNew( VLC_OBJECT(p_stream), url->psz_host,
-                                url->i_port > 0 ? url->i_port : 554 );
+                                rtsp->port );
     if( rtsp->host == NULL )
         goto error;
 
-    rtsp->url = httpd_UrlNewUnique( rtsp->host, rtsp->psz_path, NULL, NULL,
-                                    NULL );
+    rtsp->url = httpd_UrlNewUnique( rtsp->host,
+                                    url->psz_path ? url->psz_path : "/", NULL,
+                                    NULL, NULL );
     if( rtsp->url == NULL )
         goto error;
 
@@ -99,6 +108,8 @@ rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
     httpd_UrlCatch( rtsp->url, HTTPD_MSG_SETUP,    RtspCallback, (void*)rtsp );
     httpd_UrlCatch( rtsp->url, HTTPD_MSG_PLAY,     RtspCallback, (void*)rtsp );
     httpd_UrlCatch( rtsp->url, HTTPD_MSG_PAUSE,    RtspCallback, (void*)rtsp );
+    httpd_UrlCatch( rtsp->url, HTTPD_MSG_GETPARAMETER, RtspCallback,
+                    (void*)rtsp );
     httpd_UrlCatch( rtsp->url, HTTPD_MSG_TEARDOWN, RtspCallback, (void*)rtsp );
     return rtsp;
 
@@ -140,12 +151,11 @@ typedef struct rtsp_strack_t rtsp_strack_t;
 struct rtsp_session_t
 {
     rtsp_stream_t *stream;
+    uint64_t       id;
 
     /* output (id-access) */
     int            trackc;
     rtsp_strack_t *trackv;
-
-    char name[0];
 };
 
 
@@ -164,7 +174,7 @@ rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
                              const char *dst, int ttl,
                              unsigned loport, unsigned hiport )
 {
-    char urlbuf[strlen( rtsp->psz_control ) + 1 + 10];
+    char urlbuf[sizeof( "//trackID=123" ) + strlen( rtsp->psz_path )];
     rtsp_stream_id_t *id = malloc( sizeof( *id ) );
     httpd_url_t *url;
 
@@ -182,7 +192,8 @@ rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
         id->hiport = hiport;
     }
 
-    sprintf( urlbuf, "%s/trackID=%d", rtsp->psz_path, num );
+    snprintf( urlbuf, sizeof( urlbuf ), "/%s/trackID=%u", rtsp->psz_path,
+              num );
     msg_Dbg( rtsp->owner, "RTSP: adding %s\n", urlbuf );
     url = id->url = httpd_UrlNewUnique( rtsp->host, urlbuf, NULL, NULL, NULL );
 
@@ -196,6 +207,7 @@ rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
     httpd_UrlCatch( url, HTTPD_MSG_SETUP,    RtspCallbackId, (void *)id );
     httpd_UrlCatch( url, HTTPD_MSG_PLAY,     RtspCallbackId, (void *)id );
     httpd_UrlCatch( url, HTTPD_MSG_PAUSE,    RtspCallbackId, (void *)id );
+    httpd_UrlCatch( url, HTTPD_MSG_GETPARAMETER, RtspCallbackId, (void *)id );
     httpd_UrlCatch( url, HTTPD_MSG_TEARDOWN, RtspCallbackId, (void *)id );
 
     return id;
@@ -229,14 +241,16 @@ void RtspDelId( rtsp_stream_t *rtsp, rtsp_stream_id_t *id )
 
 /** rtsp must be locked */
 static
-rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp, const char *name )
+rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp )
 {
-    rtsp_session_t *s = malloc( sizeof( *s ) + strlen( name ) + 1 );
+    rtsp_session_t *s = malloc( sizeof( *s ) );
+    if( s == NULL )
+        return NULL;
 
     s->stream = rtsp;
+    s->id = rand(); /* FIXME: not enough entropy */
     s->trackc = 0;
     s->trackv = NULL;
-    strcpy( s->name, name );
 
     TAB_APPEND( rtsp->sessionc, rtsp->sessionv, s );
 
@@ -248,15 +262,22 @@ rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp, const char *name )
 static
 rtsp_session_t *RtspClientGet( rtsp_stream_t *rtsp, const char *name )
 {
+    char *end;
+    uint64_t id;
     int i;
 
     if( name == NULL )
         return NULL;
 
+    errno = 0;
+    id = strtoull( name, &end, 0x10 );
+    if( errno || *end )
+        return NULL;
+
     /* FIXME: use a hash/dictionary */
     for( i = 0; i < rtsp->sessionc; i++ )
     {
-        if( !strcmp( rtsp->sessionv[i]->name, name ) )
+        if( rtsp->sessionv[i]->id == id )
             return rtsp->sessionv[i];
     }
     return NULL;
@@ -284,7 +305,8 @@ void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session )
 /** Aggregate RTSP callback */
 static int RtspCallback( httpd_callback_sys_t *p_args,
                          httpd_client_t *cl,
-                         httpd_message_t *answer, httpd_message_t *query )
+                         httpd_message_t *answer,
+                         const httpd_message_t *query )
 {
     rtsp_stream_t *rtsp = (rtsp_stream_t *)p_args;
     const char *psz_session = NULL, *psz;
@@ -293,7 +315,6 @@ static int RtspCallback( httpd_callback_sys_t *p_args,
     {
         return VLC_SUCCESS;
     }
-    //fprintf( stderr, "RtspCallback query: type=%d\n", query->i_type );
 
     answer->i_proto = HTTPD_PROTO_RTSP;
     answer->i_version= query->i_version;
@@ -304,7 +325,7 @@ static int RtspCallback( httpd_callback_sys_t *p_args,
     if( httpd_MsgGet( query, "Require" ) != NULL )
     {
         answer->i_status = 551;
-        httpd_MsgAdd( query, "Unsupported", "%s",
+        httpd_MsgAdd( answer, "Unsupported", "%s",
                       httpd_MsgGet( query, "Require" ) );
     }
     else
@@ -312,13 +333,30 @@ static int RtspCallback( httpd_callback_sys_t *p_args,
     {
         case HTTPD_MSG_DESCRIBE:
         {
-            char *psz_sdp = SDPGenerate( rtsp->owner, rtsp->psz_control );
+            char ip[NI_MAXNUMERICHOST], *ptr;
+            char control[sizeof("rtsp://[]:12345/") + sizeof( ip )
+                            + strlen( rtsp->psz_path )];
+
+            /* Build self-referential URL */
+            httpd_ServerIP( cl, ip );
+            ptr = strchr( ip, '%' );
+            if( ptr != NULL )
+                *ptr = '\0';
+
+            if( strchr( ip, ':' ) != NULL )
+                sprintf( control, "rtsp://[%s]:%u/%s", ip, rtsp->port,
+                         ( rtsp->psz_path != NULL ) ? rtsp->psz_path : "" );
+            else
+                sprintf( control, "rtsp://%s:%u/%s", ip, rtsp->port,
+                         ( rtsp->psz_path != NULL ) ? rtsp->psz_path : "" );
+
+            ptr = SDPGenerate( rtsp->owner, control );
 
             answer->i_status = 200;
             httpd_MsgAdd( answer, "Content-Type",  "%s", "application/sdp" );
-            httpd_MsgAdd( answer, "Content-Base",  "%s", rtsp->psz_control );
-            answer->p_body = (uint8_t *)psz_sdp;
-            answer->i_body = strlen( psz_sdp );
+            httpd_MsgAdd( answer, "Content-Base",  "%s", control );
+            answer->p_body = (uint8_t *)ptr;
+            answer->i_body = strlen( ptr );
             break;
         }
 
@@ -361,7 +399,18 @@ static int RtspCallback( httpd_callback_sys_t *p_args,
 
         case HTTPD_MSG_PAUSE:
             answer->i_status = 405;
-            httpd_MsgAdd( answer, "Allow", "DESCRIBE, PLAY, TEARDOWN" );
+            httpd_MsgAdd( answer, "Allow",
+                          "DESCRIBE, TEARDOWN, PLAY, GET_PARAMETER" );
+            break;
+
+        case HTTPD_MSG_GETPARAMETER:
+            if( query->i_body > 0 )
+            {
+                answer->i_status = 451;
+                break;
+            }
+
+            answer->i_status = 200;
             break;
 
         case HTTPD_MSG_TEARDOWN:
@@ -432,17 +481,17 @@ static inline const char *parameter_next( const char *str )
 /** Non-aggregate RTSP callback */
 static int RtspCallbackId( httpd_callback_sys_t *p_args,
                            httpd_client_t *cl,
-                           httpd_message_t *answer, httpd_message_t *query )
+                           httpd_message_t *answer,
+                           const httpd_message_t *query )
 {
     rtsp_stream_id_t *id = (rtsp_stream_id_t *)p_args;
     rtsp_stream_t    *rtsp = id->stream;
     sout_stream_t    *p_stream = id->stream->owner;
-    char psz_session_init[21];
+    char psz_sesbuf[17];
     const char *psz_session, *psz;
 
     if( answer == NULL || query == NULL )
         return VLC_SUCCESS;
-    //fprintf( stderr, "RtspCallback query: type=%d\n", query->i_type );
 
     /* */
     answer->i_proto = HTTPD_PROTO_RTSP;
@@ -451,19 +500,12 @@ static int RtspCallbackId( httpd_callback_sys_t *p_args,
     answer->i_body = 0;
     answer->p_body = NULL;
 
-    /* Create new session ID if needed */
     psz_session = httpd_MsgGet( query, "Session" );
-    if( psz_session == NULL )
-    {
-        /* FIXME: should be somewhat secure randomness */
-        snprintf( psz_session_init, sizeof(psz_session_init), I64Fu,
-                  NTPtime64() + rand() );
-    }
 
     if( httpd_MsgGet( query, "Require" ) != NULL )
     {
         answer->i_status = 551;
-        httpd_MsgAdd( query, "Unsupported", "%s",
+        httpd_MsgAdd( answer, "Unsupported", "%s",
                       httpd_MsgGet( query, "Require" ) );
     }
     else
@@ -515,19 +557,25 @@ static int RtspCallbackId( httpd_callback_sys_t *p_args,
                         }
                     }
                     else
+                    if( strncmp( opt,"destination=", 12 ) == 0 )
+                    {
+                        answer->i_status = 403;
+                        b_unsupp = VLC_TRUE;
+                    }
+                    else
                     {
                     /*
                      * Every other option is unsupported:
                      *
-                     * "source" and "append" are invalid.
+                     * "source" and "append" are invalid (server-only);
+                     * "ssrc" also (as clarified per RFC2326bis).
                      *
                      * For multicast, "port", "layers", "ttl" are set by the
                      * stream output configuration.
                      *
-                     * For unicast, we do not allow "destination" as it
-                     * carries a DoS risk, and we decide on "server_port".
+                     * For unicast, we want to decide "server_port" values.
                      *
-                     * "interleaved" and "ssrc" are not implemented.
+                     * "interleaved" is not implemented.
                      */
                         b_unsupp = VLC_TRUE;
                         break;
@@ -585,8 +633,10 @@ static int RtspCallbackId( httpd_callback_sys_t *p_args,
                     vlc_mutex_lock( &rtsp->lock );
                     if( psz_session == NULL )
                     {
-                        psz_session = psz_session_init;
-                        ses = RtspClientNew( rtsp, psz_session );
+                        ses = RtspClientNew( rtsp );
+                        snprintf( psz_sesbuf, sizeof( psz_sesbuf ), I64Fx,
+                                  ses->id );
+                        psz_session = psz_sesbuf;
                     }
                     else
                     {
@@ -671,7 +721,18 @@ static int RtspCallbackId( httpd_callback_sys_t *p_args,
 
         case HTTPD_MSG_PAUSE:
             answer->i_status = 405;
-            httpd_MsgAdd( answer, "Allow", "SETUP, PLAY, TEARDOWN" );
+            httpd_MsgAdd( answer, "Allow",
+                          "SETUP, TEARDOWN, PLAY, GET_PARAMETER" );
+            break;
+
+        case HTTPD_MSG_GETPARAMETER:
+            if( query->i_body > 0 )
+            {
+                answer->i_status = 451;
+                break;
+            }
+
+            answer->i_status = 200;
             break;
 
         case HTTPD_MSG_TEARDOWN: