Exemple #1
0
/* Add a SAP announce */
static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
                             session_descriptor_t *p_session,
                             announce_method_t *p_method )
{
    int i;
    char *psz_type = "application/sdp";
    int i_header_size;
    char *psz_head;
    vlc_bool_t b_found = VLC_FALSE;
    sap_session_t *p_sap_session;
    mtime_t i_hash;

    vlc_mutex_lock( &p_sap->object_lock );

    /* If needed, build the SDP */
    if( !p_session->psz_sdp )
    {
        if ( SDPGenerate( p_sap, p_session ) != VLC_SUCCESS )
        {
            vlc_mutex_unlock( &p_sap->object_lock );
            return VLC_EGENERIC;
        }
    }

    if( !p_method->psz_address )
    {
        if( p_method->i_ip_version == 6 )
        {
            char sz_scope;
            if( p_method->psz_ipv6_scope != NULL )
            {
                sz_scope = *p_method->psz_ipv6_scope;
            }
            else
            {
                sz_scope = DEFAULT_IPV6_SCOPE;
            }
            p_method->psz_address = (char*)malloc( 30*sizeof(char ));
            sprintf( p_method->psz_address, "%s%c%s",
                            SAP_IPV6_ADDR_1, sz_scope, SAP_IPV6_ADDR_2 );
        }
        else
        {
            /* IPv4 */
            p_method->psz_address = (char*)malloc( 15*sizeof(char) );
            snprintf(p_method->psz_address, 15, SAP_IPV4_ADDR );
        }
    }
    msg_Dbg( p_sap, "using SAP address: %s",p_method->psz_address);

    /* XXX: Check for dupes */
    p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t));

    p_sap_session->psz_sdp = strdup( p_session->psz_sdp );
    p_sap_session->i_last = 0;

    /* Add the address to the buffer */
    for( i = 0; i< p_sap->i_addresses; i++)
    {
        if( !strcmp( p_method->psz_address,
             p_sap->pp_addresses[i]->psz_address ) )
        {
            p_sap_session->p_address = p_sap->pp_addresses[i];
            b_found = VLC_TRUE;
            break;
        }
    }
    if( b_found == VLC_FALSE )
    {
        sap_address_t *p_address = (sap_address_t *)
                                    malloc( sizeof(sap_address_t) );
        if( !p_address )
        {
            msg_Err( p_sap, "out of memory" );
            return VLC_ENOMEM;
        }
        p_address->psz_address = strdup( p_method->psz_address );
        p_address->i_ip_version = p_method->i_ip_version;
        p_address->i_port  =  9875;
        p_address->i_wfd = net_OpenUDP( p_sap, "", 0,
                                        p_address->psz_address,
                                        p_address->i_port );

        if( p_sap->b_control == VLC_TRUE )
        {
            p_address->i_rfd = net_OpenUDP( p_sap, p_method->psz_address,
                                            p_address->i_port,
                                            "", 0 );
            p_address->i_buff = 0;
            p_address->b_enabled = VLC_TRUE;
            p_address->b_ready = VLC_FALSE;
            p_address->i_limit = 10000; /* 10000 bps */
            p_address->t1 = 0;
        }
        else
        {
            p_address->b_enabled = VLC_TRUE;
            p_address->b_ready = VLC_TRUE;
            p_address->i_interval = config_GetInt( p_sap,"sap-interval");
        }

        if( p_address->i_wfd == -1 || (p_address->i_rfd == -1
                                        && p_sap->b_control ) )
        {
            msg_Warn( p_sap, "disabling address" );
            p_address->b_enabled = VLC_FALSE;
        }

        INSERT_ELEM( p_sap->pp_addresses,
                     p_sap->i_addresses,
                     p_sap->i_addresses,
                     p_address );
        p_sap_session->p_address = p_address;
    }

    /* Build the SAP Headers */
    i_header_size = ( p_method->i_ip_version == 6 ? 20 : 8 ) + strlen( psz_type ) + 1;
    psz_head = (char *) malloc( i_header_size * sizeof( char ) );
    if( ! psz_head )
    {
        msg_Err( p_sap, "out of memory" );
        return VLC_ENOMEM;
    }

    psz_head[0] = 0x20; /* Means SAPv1, IPv4, not encrypted, not compressed */
    psz_head[1] = 0x00; /* No authentification length */

    i_hash = mdate();
    psz_head[2] = (i_hash & 0xFF00) >> 8; /* Msg id hash */
    psz_head[3] = (i_hash & 0xFF);        /* Msg id hash 2 */

    if( p_method->i_ip_version == 6 )
    {
        /* in_addr_t ip_server = inet_addr( ip ); */
        psz_head[0] |= 0x10; /* Set IPv6 */

        psz_head[4] = 0x01; /* Source IP  FIXME: we should get the real address */
        psz_head[5] = 0x02; /* idem */
        psz_head[6] = 0x03; /* idem */
        psz_head[7] = 0x04; /* idem */

        psz_head[8] = 0x01; /* Source IP  FIXME: we should get the real address */
        psz_head[9] = 0x02; /* idem */
        psz_head[10] = 0x03; /* idem */
        psz_head[11] = 0x04; /* idem */

        psz_head[12] = 0x01; /* Source IP  FIXME: we should get the real address */
        psz_head[13] = 0x02; /* idem */
        psz_head[14] = 0x03; /* idem */
        psz_head[15] = 0x04; /* idem */

        psz_head[16] = 0x01; /* Source IP  FIXME: we should get the real address */
        psz_head[17] = 0x02; /* idem */
        psz_head[18] = 0x03; /* idem */
        psz_head[19] = 0x04; /* idem */

        strncpy( psz_head + 20, psz_type, 15 );
    }
    else
    {
        /* in_addr_t ip_server = inet_addr( ip) */
        /* Source IP  FIXME: we should get the real address */
        psz_head[4] = 0x01; /* ip_server */
        psz_head[5] = 0x02; /* ip_server>>8 */
        psz_head[6] = 0x03; /* ip_server>>16 */
        psz_head[7] = 0x04; /* ip_server>>24 */

        strncpy( psz_head + 8, psz_type, 15 );
    }

    psz_head[ i_header_size-1 ] = '\0';
    p_sap_session->i_length = i_header_size + strlen( p_sap_session->psz_sdp);

    p_sap_session->psz_data = (char *)malloc( sizeof(char)*
                                              p_sap_session->i_length );

    /* Build the final message */
    memcpy( p_sap_session->psz_data, psz_head, i_header_size );
    memcpy( p_sap_session->psz_data+i_header_size, p_sap_session->psz_sdp,
            strlen( p_sap_session->psz_sdp) );

    free( psz_head );

    /* Enqueue the announce */
    INSERT_ELEM( p_sap->pp_sessions,
                 p_sap->i_sessions,
                 p_sap->i_sessions,
                 p_sap_session );
    msg_Dbg( p_sap,"Addresses: %i  Sessions: %i",
                   p_sap->i_addresses,p_sap->i_sessions);

    /* Remember the SAP session for later deletion */
    p_session->p_sap = p_sap_session;

    vlc_mutex_unlock( &p_sap->object_lock );

    return VLC_SUCCESS;
}
Exemple #2
0
/** RTSP requests handler
 * @param id selected track for non-aggregate URLs,
 *           NULL for aggregate URLs
 */
static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                        httpd_client_t *cl,
                        httpd_message_t *answer,
                        const httpd_message_t *query )
{
    sout_stream_t *p_stream = rtsp->owner;
    char psz_sesbuf[17];
    const char *psz_session = NULL, *psz;
    char control[sizeof("rtsp://[]:12345") + NI_MAXNUMERICHOST
                  + strlen( rtsp->psz_path )];
    time_t now;

    time (&now);

    if( answer == NULL || query == NULL || cl == NULL )
        return VLC_SUCCESS;
    else
    {
        /* Build self-referential control URL */
        char ip[NI_MAXNUMERICHOST], *ptr;

        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 );
        else
            sprintf( control, "rtsp://%s:%u%s", ip, rtsp->port,
                     rtsp->psz_path );
    }

    /* */
    answer->i_proto = HTTPD_PROTO_RTSP;
    answer->i_version= 0;
    answer->i_type   = HTTPD_MSG_ANSWER;
    answer->i_body = 0;
    answer->p_body = NULL;

    httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING );

    /* Date: is always allowed, and sometimes mandatory with RTSP/2.0. */
    struct tm ut;
    if (gmtime_r (&now, &ut) != NULL)
    {   /* RFC1123 format, GMT is mandatory */
        static const char wdays[7][4] = {
            "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
        static const char mons[12][4] = {
            "Jan", "Feb", "Mar", "Apr", "May", "Jun",
            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        httpd_MsgAdd (answer, "Date", "%s, %02u %s %04u %02u:%02u:%02u GMT",
                      wdays[ut.tm_wday], ut.tm_mday, mons[ut.tm_mon],
                      1900 + ut.tm_year, ut.tm_hour, ut.tm_min, ut.tm_sec);
    }

    if( query->i_proto != HTTPD_PROTO_RTSP )
    {
        answer->i_status = 505;
    }
    else
    if( httpd_MsgGet( query, "Require" ) != NULL )
    {
        answer->i_status = 551;
        httpd_MsgAdd( answer, "Unsupported", "%s",
                      httpd_MsgGet( query, "Require" ) );
    }
    else
    switch( query->i_type )
    {
        case HTTPD_MSG_DESCRIBE:
        {   /* Aggregate-only */
            if( id != NULL )
            {
                answer->i_status = 460;
                break;
            }

            answer->i_status = 200;
            httpd_MsgAdd( answer, "Content-Type",  "%s", "application/sdp" );
            httpd_MsgAdd( answer, "Content-Base",  "%s", control );
            answer->p_body = (uint8_t *)SDPGenerate( rtsp->owner, control );
            if( answer->p_body != NULL )
                answer->i_body = strlen( (char *)answer->p_body );
            else
                answer->i_status = 500;
            break;
        }

        case HTTPD_MSG_SETUP:
            /* Non-aggregate-only */
            if( id == NULL )
            {
                answer->i_status = 459;
                break;
            }

            psz_session = httpd_MsgGet( query, "Session" );
            answer->i_status = 461;

            for( const char *tpt = httpd_MsgGet( query, "Transport" );
                 tpt != NULL;
                 tpt = transport_next( tpt ) )
            {
                bool b_multicast = true, b_unsupp = false;
                unsigned loport = 5004, hiport = 5005; /* from RFC3551 */

                /* Check transport protocol. */
                /* Currently, we only support RTP/AVP over UDP */
                if( strncmp( tpt, "RTP/AVP", 7 ) )
                    continue;
                tpt += 7;
                if( strncmp( tpt, "/UDP", 4 ) == 0 )
                    tpt += 4;
                if( strchr( ";,", *tpt ) == NULL )
                    continue;

                /* Parse transport options */
                for( const char *opt = parameter_next( tpt );
                     opt != NULL;
                     opt = parameter_next( opt ) )
                {
                    if( strncmp( opt, "multicast", 9 ) == 0)
                        b_multicast = true;
                    else
                    if( strncmp( opt, "unicast", 7 ) == 0 )
                        b_multicast = false;
                    else
                    if( sscanf( opt, "client_port=%u-%u", &loport, &hiport )
                                == 2 )
                        ;
                    else
                    if( strncmp( opt, "mode=", 5 ) == 0 )
                    {
                        if( strncasecmp( opt + 5, "play", 4 )
                         && strncasecmp( opt + 5, "\"PLAY\"", 6 ) )
                        {
                            /* Not playing?! */
                            b_unsupp = true;
                            break;
                        }
                    }
                    else
                    if( strncmp( opt,"destination=", 12 ) == 0 )
                    {
                        answer->i_status = 403;
                        b_unsupp = true;
                    }
                    else
                    {
                    /*
                     * Every other option is unsupported:
                     *
                     * "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 want to decide "server_port" values.
                     *
                     * "interleaved" is not implemented.
                     */
                        b_unsupp = true;
                        break;
                    }
                }

                if( b_unsupp )
                    continue;

                if( b_multicast )
                {
                    const char *dst = id->dst;
                    if( dst == NULL )
                        continue;

                    if( psz_session == NULL )
                    {
                        /* Create a dummy session ID */
                        snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%d",
                                  rand() );
                        psz_session = psz_sesbuf;
                    }
                    answer->i_status = 200;

                    httpd_MsgAdd( answer, "Transport",
                                  "RTP/AVP/UDP;destination=%s;port=%u-%u;"
                                  "ttl=%d;mode=play",
                                  dst, id->loport, id->hiport,
                                  ( id->ttl > 0 ) ? id->ttl : 1 );
                }
                else
                {
                    char ip[NI_MAXNUMERICHOST], src[NI_MAXNUMERICHOST];
                    rtsp_session_t *ses = NULL;
                    rtsp_strack_t track = { id->sout_id, -1, false };
                    int sport;

                    if( httpd_ClientIP( cl, ip ) == NULL )
                    {
                        answer->i_status = 500;
                        continue;
                    }

                    track.fd = net_ConnectDgram( p_stream, ip, loport, -1,
                                                 IPPROTO_UDP );
                    if( track.fd == -1 )
                    {
                        msg_Err( p_stream,
                                 "cannot create RTP socket for %s port %u",
                                 ip, loport );
                        answer->i_status = 500;
                        continue;
                    }

                    net_GetSockAddress( track.fd, src, &sport );

                    vlc_mutex_lock( &rtsp->lock );
                    if( psz_session == NULL )
                    {
                        ses = RtspClientNew( rtsp );
                        snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%"PRIx64,
                                  ses->id );
                        psz_session = psz_sesbuf;
                    }
                    else
                    {
                        /* FIXME: we probably need to remove an access out,
                         * if there is already one for the same ID */
                        ses = RtspClientGet( rtsp, psz_session );
                        if( ses == NULL )
                        {
                            answer->i_status = 454;
                            vlc_mutex_unlock( &rtsp->lock );
                            continue;
                        }
                    }

                    INSERT_ELEM( ses->trackv, ses->trackc, ses->trackc,
                                 track );
                    vlc_mutex_unlock( &rtsp->lock );

                    httpd_ServerIP( cl, ip );

                    if( strcmp( src, ip ) )
                    {
                        /* Specify source IP if it is different from the RTSP
                         * control connection server address */
                        char *ptr = strchr( src, '%' );
                        if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */

                        httpd_MsgAdd( answer, "Transport",
                                      "RTP/AVP/UDP;unicast;source=%s;"
                                      "client_port=%u-%u;server_port=%u-%u;"
                                      "ssrc=%08X;mode=play",
                                      src, loport, loport + 1, sport,
                                      sport + 1, id->ssrc );
                    }
                    else
                    {
                        httpd_MsgAdd( answer, "Transport",
                                      "RTP/AVP/UDP;unicast;"
                                      "client_port=%u-%u;server_port=%u-%u;"
                                      "ssrc=%08X;mode=play",
                                      loport, loport + 1, sport, sport + 1,
                                      id->ssrc );
                    }

                    answer->i_status = 200;
                }
                break;
            }
            break;

        case HTTPD_MSG_PLAY:
        {
            rtsp_session_t *ses;
            answer->i_status = 200;

            psz_session = httpd_MsgGet( query, "Session" );
            const char *range = httpd_MsgGet (query, "Range");
            if (range && strncmp (range, "npt=", 4))
            {
                answer->i_status = 501;
                break;
            }

            vlc_mutex_lock( &rtsp->lock );
            ses = RtspClientGet( rtsp, psz_session );
            if( ses != NULL )
            {
                /* FIXME: we really need to limit the number of tracks... */
                char info[ses->trackc * ( strlen( control )
                              + sizeof("url=/trackID=123;seq=65535, ") ) + 1];
                size_t infolen = 0;

                for( int i = 0; i < ses->trackc; i++ )
                {
                    rtsp_strack_t *tr = ses->trackv + i;
                    if( ( id == NULL ) || ( tr->id == id->sout_id ) )
                    {
                        if( !tr->playing )
                        {
                            tr->playing = true;
                            rtp_add_sink( tr->id, tr->fd, false );
                        }
                        infolen += sprintf( info + infolen,
                                            "url=%s/trackID=%u;seq=%u, ",
                                            control,
                                            rtp_get_num( tr->id ),
                                            rtp_get_seq( tr->id ) );
                    }
                }
                if( infolen > 0 )
                {
                    info[infolen - 2] = '\0'; /* remove trailing ", " */
                    httpd_MsgAdd( answer, "RTP-Info", "%s", info );
                }
            }
            vlc_mutex_unlock( &rtsp->lock );

            if( httpd_MsgGet( query, "Scale" ) != NULL )
                httpd_MsgAdd( answer, "Scale", "1." );
            break;
        }

        case HTTPD_MSG_PAUSE:
            answer->i_status = 405;
            httpd_MsgAdd( answer, "Allow",
                          "%s, TEARDOWN, PLAY, GET_PARAMETER",
                          ( id != NULL ) ? "SETUP" : "DESCRIBE" );
            break;

        case HTTPD_MSG_GETPARAMETER:
            if( query->i_body > 0 )
            {
                answer->i_status = 451;
                break;
            }

            psz_session = httpd_MsgGet( query, "Session" );
            answer->i_status = 200;
            break;

        case HTTPD_MSG_TEARDOWN:
        {
            rtsp_session_t *ses;

            answer->i_status = 200;

            psz_session = httpd_MsgGet( query, "Session" );

            vlc_mutex_lock( &rtsp->lock );
            ses = RtspClientGet( rtsp, psz_session );
            if( ses != NULL )
            {
                if( id == NULL ) /* Delete the entire session */
                    RtspClientDel( rtsp, ses );
                else /* Delete one track from the session */
                for( int i = 0; i < ses->trackc; i++ )
                {
                    if( ses->trackv[i].id == id->sout_id )
                    {
                        rtp_del_sink( id->sout_id, ses->trackv[i].fd );
                        REMOVE_ELEM( ses->trackv, ses->trackc, i );
                    }
                }
            }
            vlc_mutex_unlock( &rtsp->lock );
            break;
        }

        default:
            return VLC_EGENERIC;
    }

    if( psz_session )
        httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session );

    httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
    httpd_MsgAdd( answer, "Cache-Control", "no-cache" );

    psz = httpd_MsgGet( query, "Cseq" );
    if( psz != NULL )
        httpd_MsgAdd( answer, "Cseq", "%s", psz );
    psz = httpd_MsgGet( query, "Timestamp" );
    if( psz != NULL )
        httpd_MsgAdd( answer, "Timestamp", "%s", psz );

    return VLC_SUCCESS;
}