Changeset 14fb31d9ad306f08d77bd234957651b32712dad2

Show
Ignore:
Timestamp:
11/02/07 17:08:56 (2 years ago)
Author:
Rémi Denis-Courmont <rem@videolan.org>
git-committer:
Rémi Denis-Courmont <rem@videolan.org> 1171210136 +0000
git-parent:

[60a679f5c4bb7c9bb82a0a8f0d8581f815b21df5]

git-author:
Rémi Denis-Courmont <rem@videolan.org> 1171210136 +0000
Message:

Improve SAP parser

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • modules/services_discovery/sap.c

    r496a542 r14fb31d  
    33 ***************************************************************************** 
    44 * Copyright (C) 2004-2005 the VideoLAN team 
     5 * Copyright © 2007 Rémi Denis-Courmont 
    56 * $Id$ 
    67 * 
    78 * Authors: Clément Stenac <zorglub@videolan.org> 
     9 *          Rémi Denis-Courmont 
    810 * 
    911 * This program is free software; you can redistribute it and/or modify 
     
    165167typedef struct sap_announce_t sap_announce_t; 
    166168 
    167 /* The structure that contains sdp information */ 
    168 struct  sdp_t 
    169 
    170     char *psz_sdp; 
    171  
    172     /* s= field */ 
    173     char *psz_sessionname; 
    174  
    175     /* Raw m= and c= fields */ 
    176     char *psz_connection; 
    177     char *psz_media; 
    178  
    179     /* o field */ 
    180     char *psz_username; 
    181     char *psz_network_type; 
    182     char *psz_address_type; 
    183     char *psz_address; 
    184     int64_t i_session_id; 
    185  
    186     /* "computed" URI */ 
    187     char *psz_uri; 
    188  
    189     int           i_in; /* IP version */ 
    190  
    191     int           i_media; 
    192     int           i_media_type; 
    193  
     169 
     170struct sdp_media_t 
     171
     172    struct sdp_t           *parent; 
     173    char                   *fmt; 
     174    struct sockaddr_storage addr; 
     175    socklen_t               addrlen; 
     176    unsigned                n_addr; 
    194177    int           i_attributes; 
    195178    attribute_t  **pp_attributes; 
    196179}; 
    197180 
     181 
     182/* The structure that contains sdp information */ 
     183struct  sdp_t 
     184{ 
     185    char *psz_sdp; 
     186 
     187    /* o field */ 
     188    char     username[64]; 
     189    uint64_t session_id; 
     190    uint64_t session_version; 
     191    unsigned orig_ip_version; 
     192    char     orig_host[1024]; 
     193 
     194    /* s= field */ 
     195    char *psz_sessionname; 
     196 
     197    /* old cruft */ 
     198    /* "computed" URI */ 
     199    char *psz_uri; 
     200    int           i_media_type; 
     201 
     202    /* a= global attributes */ 
     203    int           i_attributes; 
     204    attribute_t  **pp_attributes; 
     205 
     206    /* medias (well, we only support one atm) */ 
     207    unsigned            mediac; 
     208    struct sdp_media_t  mediav[1]; 
     209}; 
     210 
    198211struct attribute_t 
    199212{ 
    200     char *psz_field
    201     char *psz_value
     213    const char *value
     214    char name[0]
    202215}; 
    203216 
     
    262275 
    263276/* Helper functions */ 
    264     static char *GetAttribute( sdp_t *p_sdp, const char *psz_search ); 
     277    static inline attribute_t *MakeAttribute (const char *str); 
     278    static const char *GetAttribute (attribute_t **tab, unsigned n, const char *name); 
     279    static inline void FreeAttribute (attribute_t *a); 
     280 
    265281    static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 ); 
    266282    static int InitSocket( services_discovery_t *p_sd, const char *psz_address, int i_port ); 
     
    378394    } 
    379395 
    380     if( p_sdp->i_media > 1 ) 
     396    if( p_sdp->mediac > 1 ) 
    381397    { 
    382398        goto error; 
     
    572588    p_parent_input->psz_uri = strdup( p_sdp->psz_uri ); 
    573589    FREENULL( p_parent_input->psz_name ); 
    574     p_parent_input->psz_name = strdup( EnsureUTF8( p_sdp->psz_sessionname ) ); 
     590    p_parent_input->psz_name = strdup( p_sdp->psz_sessionname ); 
    575591    p_parent_input->i_type = ITEM_TYPE_NET; 
    576592 
     
    703719 
    704720    /* Multi-media or no-parse -> pass to LIVE.COM */ 
    705     if( p_sdp->i_media > 1 || ( p_sdp->i_media_type != 14 && 
     721    if( p_sdp->mediac > 1 || ( p_sdp->i_media_type != 14 && 
    706722                                p_sdp->i_media_type != 32 && 
    707723                                p_sdp->i_media_type != 33) || 
     
    732748        } 
    733749    } 
    734     /* Add item */ 
    735     if( p_sdp->i_media > 1 ) 
    736     { 
    737         msg_Dbg( p_sd, "passing to liveMedia" ); 
    738     } 
    739750 
    740751    CreateAnnounce( p_sd, i_hash, p_sdp ); 
     
    749760    input_item_t *p_input; 
    750761    playlist_item_t     *p_item, *p_child; 
    751     char *psz_value; 
     762    const char *psz_value; 
    752763    sap_announce_t *p_sap = (sap_announce_t *)malloc( 
    753764                                        sizeof(sap_announce_t ) ); 
     
    758769    p_sys = p_sd->p_sys; 
    759770 
    760     EnsureUTF8( p_sdp->psz_sessionname ); 
    761771    p_sap->i_last = mdate(); 
    762772    p_sap->i_hash = i_hash; 
     
    778788        input_ItemAddOption( p_input, ":access-filter=timeshift" ); 
    779789 
    780     psz_value = GetAttribute( p_sap->p_sdp, "tool" ); 
     790    psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "tool" ); 
    781791    if( psz_value != NULL ) 
    782792    { 
    783793        input_ItemAddInfo( p_input, _("Session"),_("Tool"), psz_value ); 
    784794    } 
    785     if( strcmp( p_sdp->psz_username, "-" ) ) 
     795    if( strcmp( p_sdp->username, "-" ) ) 
    786796    { 
    787797        input_ItemAddInfo( p_input, _("Session"), 
    788                                 _("User"), p_sdp->psz_username ); 
     798                                _("User"), p_sdp->username ); 
    789799    } 
    790800 
    791801    /* Handle group */ 
    792     psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" ); 
     802    psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "x-plgroup" ); 
    793803    if( psz_value == NULL ) 
    794         psz_value = GetAttribute( p_sap->p_sdp, "plgroup" ); 
     804        psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "plgroup" ); 
    795805 
    796806    if( psz_value != NULL ) 
    797807    { 
    798         EnsureUTF8( psz_value ); 
    799  
    800808        p_child = playlist_ChildSearchName( p_sys->p_node_cat, psz_value ); 
    801809 
     
    829837} 
    830838 
    831 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search ) 
    832 { 
    833     int i; 
    834  
    835     for( i = 0 ; i< p_sdp->i_attributes; i++ ) 
    836     { 
    837         if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search, 
    838                       strlen( p_sdp->pp_attributes[i]->psz_field ) ) ) 
    839         { 
    840             return p_sdp->pp_attributes[i]->psz_value; 
    841         } 
    842     } 
    843     return NULL; 
    844 } 
    845  
    846  
    847839/* Fill p_sdp->psz_uri */ 
    848840static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) 
    849841{ 
    850     char *psz_eof = NULL; 
    851     char *psz_parse = NULL; 
     842    if (p_sdp->mediac != 1) 
     843        return VLC_EGENERIC; 
     844 
    852845    char psz_uri[1026]; 
    853     char *psz_proto = NULL; 
    854     int i_port = 0; 
    855  
    856     /* Parse c= field */ 
    857     if( p_sdp->psz_connection ) 
    858     { 
    859         char hostname[1024]; 
    860         int ipv; 
    861  
    862         /* 
    863          * NOTE: we ignore the TTL parameter on-purpose, as some SAP 
    864          * advertisers don't include it (and it is utterly useless). 
    865          */ 
    866         if (sscanf (p_sdp->psz_connection, "IN IP%d %1023[^/]", &ipv, 
    867                     hostname) != 2) 
    868         { 
    869             msg_Warn (p_obj, "unable to parse c field: \"%s\"", 
    870                       p_sdp->psz_connection); 
    871             return VLC_EGENERIC; 
    872         } 
    873  
    874         switch (ipv) 
    875         { 
    876             case 4: 
    877             case 6: 
    878                 break; 
    879  
    880             default: 
    881                 msg_Warn (p_obj, "unknown IP version %d", ipv); 
    882                 return VLC_EGENERIC; 
    883         } 
    884  
    885         if (strchr (hostname, ':') != NULL) 
    886             sprintf (psz_uri, "[%s]", hostname); 
    887         else 
    888             strcpy (psz_uri, hostname); 
    889     } 
     846    const char *host; 
     847    int port; 
     848 
     849    psz_uri[0] = '['; 
     850    if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav[0].addr), 
     851                         p_sdp->mediav[0].addrlen, psz_uri + 1, 
     852                         sizeof (psz_uri) - 2, &port, NI_NUMERICHOST)) 
     853        return VLC_EGENERIC; 
     854 
     855    if (strchr (psz_uri + 1, ':')) 
     856    { 
     857        host = psz_uri; 
     858        psz_uri[strlen (psz_uri)] = ']'; 
     859    } 
     860    else 
     861        host = psz_uri + 1; 
    890862 
    891863    /* Parse m= field */ 
    892     if( p_sdp->psz_media ) 
    893     { 
    894         psz_parse = p_sdp->psz_media; 
    895  
    896         psz_eof = strchr( psz_parse, ' ' ); 
    897  
    898         if( psz_eof ) 
    899         { 
    900             *psz_eof = '\0'; 
    901  
    902             /* 
    903              * That's ugly. We should go through every media, and make sure 
    904              * at least one of them is audio or video. In the mean time, I 
    905              * need to accept data too. 
    906              */ 
    907             if( strncmp( psz_parse, "audio", 5 ) 
    908              && strncmp( psz_parse, "video", 5 ) 
    909              && strncmp( psz_parse, "data", 4 ) ) 
    910             { 
    911                 msg_Warn( p_obj, "unhandled media type \"%s\"", psz_parse ); 
    912                 return VLC_EGENERIC; 
    913             } 
    914  
    915             psz_parse = psz_eof + 1; 
    916         } 
    917         else 
    918         { 
    919             msg_Warn( p_obj, "unable to parse m field (1)"); 
    920             return VLC_EGENERIC; 
    921         } 
    922  
    923         psz_eof = strchr( psz_parse, ' ' ); 
    924  
    925         if( psz_eof ) 
    926         { 
    927             *psz_eof = '\0'; 
    928  
    929             /* FIXME : multiple port ! */ 
    930             i_port = atoi( psz_parse ); 
    931  
    932             if( i_port <= 0 || i_port >= 65536 ) 
    933             { 
    934                 msg_Warn( p_obj, "invalid transport port %i", i_port ); 
    935             } 
    936  
    937             psz_parse = psz_eof + 1; 
    938         } 
    939         else 
    940         { 
    941             msg_Warn( p_obj, "unable to parse m field (2)"); 
    942             return VLC_EGENERIC; 
    943         } 
    944  
    945         psz_eof = strchr( psz_parse, ' ' ); 
    946  
    947         if( psz_eof ) 
    948         { 
    949             *psz_eof = '\0'; 
    950             psz_proto = strdup( psz_parse ); 
    951  
    952             psz_parse = psz_eof + 1; 
    953             p_sdp->i_media_type = atoi( psz_parse ); 
    954  
    955         } 
    956         else 
    957         { 
    958             msg_Dbg( p_obj, "incorrect m field, %s", p_sdp->psz_media ); 
    959             p_sdp->i_media_type = 33; 
    960             psz_proto = strdup( psz_parse ); 
    961         } 
    962     } 
    963  
    964     if( psz_proto && !strncmp( psz_proto, "RTP/AVP", 7 ) ) 
    965     { 
    966         free( psz_proto ); 
    967         psz_proto = strdup( "rtp" ); 
    968     } 
    969     if( psz_proto && !strncasecmp( psz_proto, "UDP", 3 ) ) 
    970     { 
    971         free( psz_proto ); 
    972         psz_proto = strdup( "udp" ); 
    973     } 
    974  
    975     if( i_port == 0 ) 
    976     { 
    977         i_port = 1234; 
     864    char *sdp_proto = strdup (p_sdp->mediav[0].fmt); 
     865    if (sdp_proto == NULL) 
     866        return VLC_ENOMEM; 
     867 
     868    char *subtype = strchr (sdp_proto, ' '); 
     869    if (sdp_proto == NULL) 
     870    { 
     871        msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto); 
     872        p_sdp->i_media_type = 0; 
     873    } 
     874    else 
     875    { 
     876        *subtype++ = '\0'; 
     877        p_sdp->i_media_type = atoi (subtype); 
     878    } 
     879    if (p_sdp->i_media_type == 0) 
     880         p_sdp->i_media_type = 33; 
     881 
     882    static const char proto_match[] = 
     883        "udp\0"             "udp\0" 
     884        "RTP/AVP\0"         "rtp\0" 
     885        "UDPLite/RTP/AVP\0" "udplite\0" 
     886        "DCCP/RTP/AVP\0"    "dccp\0" 
     887        "TCP/RTP/AVP\0"     "tcp\0" 
     888        "\0"; 
     889 
     890    const char *vlc_proto = NULL; 
     891    for (const char *proto = proto_match; *proto;) 
     892    { 
     893        if (strcasecmp (proto, sdp_proto)) 
     894        { 
     895            vlc_proto = proto + strlen (proto) + 1; 
     896            break; 
     897        } 
     898        proto += strlen (proto) + 1; 
     899        proto += strlen (proto) + 1; 
     900    } 
     901 
     902    free (sdp_proto); 
     903    if (vlc_proto == NULL) 
     904    { 
     905        msg_Dbg (p_obj, "unknown SDP media protocol: %s", 
     906                 p_sdp->mediav[0].fmt); 
     907        return VLC_EGENERIC; 
    978908    } 
    979909 
    980910    /* handle SSM case */ 
    981     psz_parse = GetAttribute( p_sdp, "source-filter" ); 
     911    const char *sfilter = GetAttribute( p_sdp->mediav[0].pp_attributes, 
     912                                        p_sdp->mediav[0].i_attributes, 
     913                                        "source-filter" ); 
     914    if (sfilter == NULL) 
     915        sfilter = GetAttribute( p_sdp->pp_attributes, p_sdp->i_attributes, 
     916                                "source-filter" ); 
     917 
    982918    char psz_source[258] = ""; 
    983     if (psz_parse != NULL) 
     919    if (sfilter != NULL) 
    984920    { 
    985921        char psz_source_ip[256]; 
    986922 
    987         if (sscanf (psz_parse, " incl IN IP%*c %*s %255s ", psz_source_ip) == 1) 
     923        if (sscanf (sfilter, " incl IN IP%*c %*s %255s ", psz_source_ip) == 1) 
    988924        { 
    989925            if (strchr (psz_source_ip, ':') != NULL) 
     
    994930    } 
    995931 
    996     asprintf( &p_sdp->psz_uri, "%s://%s@%s:%i", psz_proto, psz_source, 
    997               psz_uri, i_port ); 
    998  
    999     FREENULL( psz_proto ); 
     932    asprintf( &p_sdp->psz_uri, "%s://%s@%s:%i", vlc_proto, psz_source, 
     933              host, port ); 
     934 
    1000935    return VLC_SUCCESS; 
    1001936} 
     937 
     938 
     939static int ParseSDPConnection (const char *str, struct sockaddr_storage *addr, 
     940                               socklen_t *addrlen, unsigned *number) 
     941{ 
     942    char host[60]; 
     943    unsigned fam, n1, n2; 
     944 
     945    int res = sscanf (str, "IN IP%u %59[^/]/%u/%u", &fam, host, &n1, &n2); 
     946    if (res < 2) 
     947        return -1; 
     948 
     949    switch (fam) 
     950    { 
     951#ifdef AF_INET6 
     952        case 6: 
     953            addr->ss_family = AF_INET6; 
     954# ifdef HAVE_SA_LEN 
     955            addr->ss_len = 
     956# endif 
     957           *addrlen = sizeof (struct sockaddr_in6); 
     958 
     959            if (inet_pton (AF_INET6, host, 
     960                           &((struct sockaddr_in6 *)addr)->sin6_addr) <= 0) 
     961                return -1; 
     962 
     963            *number = (res >= 3) ? n1 : 1; 
     964            break; 
     965#endif 
     966 
     967        case 4: 
     968            addr->ss_family = AF_INET; 
     969# ifdef HAVE_SA_LEN 
     970            addr->ss_len = 
     971# endif 
     972           *addrlen = sizeof (struct sockaddr_in); 
     973 
     974            if (inet_pton (AF_INET, host, 
     975                           &((struct sockaddr_in *)addr)->sin_addr) <= 0) 
     976                return -1; 
     977 
     978            *number = (res >= 4) ? n2 : 1; 
     979            break; 
     980 
     981        default: 
     982            return -1; 
     983    } 
     984    return 0; 
     985} 
     986 
    1002987 
    1003988/*********************************************************************** 
     
    1008993static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) 
    1009994{ 
    1010     sdp_t *p_sdp; 
    1011     vlc_bool_t b_invalid = VLC_FALSE; 
    1012     vlc_bool_t b_end = VLC_FALSE; 
    1013995    if( psz_sdp == NULL ) 
    1014     { 
    1015996        return NULL; 
    1016     } 
    1017  
    1018     if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' ) 
    1019     { 
    1020         msg_Warn( p_obj, "bad packet" ); 
     997 
     998    sdp_t *p_sdp = calloc (1, sizeof (*p_sdp)); 
     999    if (p_sdp == NULL) 
    10211000        return NULL; 
    1022     } 
    1023  
    1024     p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) ); 
    1025     if( p_sdp == NULL ) 
    1026         return NULL; 
    1027  
    1028     /* init to 0 */ 
    1029     memset( p_sdp, 0, sizeof( sdp_t ) ); 
    1030  
    1031     p_sdp->psz_sdp = strdup( psz_sdp ); 
    1032     if( p_sdp->psz_sdp == NULL ) 
    1033     { 
    1034         free( p_sdp ); 
    1035         return NULL; 
    1036     } 
    1037  
    1038     while( *psz_sdp != '\0' && b_end == VLC_FALSE  ) 
    1039     { 
    1040         char *psz_eol = NULL; 
    1041         char *psz_eof = NULL; 
    1042         char *psz_parse = NULL; 
    1043         char *psz_sess_id = NULL; 
    1044  
    1045         while( *psz_sdp == '\r' || *psz_sdp == '\n' || 
    1046                *psz_sdp == ' ' || *psz_sdp == '\t' ) 
    1047         { 
    1048             psz_sdp++; 
    1049         } 
    1050  
    1051         if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL ) 
    1052         { 
    1053             psz_eol = (char *)psz_sdp + strlen( psz_sdp ); 
    1054             b_end = VLC_TRUE; 
    1055         } 
    1056         if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' ) 
    1057         { 
    1058             psz_eol--; 
    1059         } 
    1060  
    1061         if( psz_eol <= psz_sdp ) 
    1062         { 
    1063             break; 
    1064         } 
    1065         *psz_eol++ = '\0'; 
    1066  
    1067         /* no space allowed between fields */ 
    1068         if( psz_sdp[1] != '=' ) 
    1069         { 
    1070             msg_Warn( p_obj, "invalid packet" ) ; 
    1071             FreeSDP( p_sdp ); p_sdp = NULL; 
    1072             return NULL; 
    1073         } 
    1074  
    1075         /* Now parse each line */ 
    1076         switch( psz_sdp[0] ) 
    1077         { 
    1078             case( 'v' ): 
     1001 
     1002    char expect = 'V'; 
     1003    struct sockaddr_storage glob_addr; 
     1004    memset (&glob_addr, 0, sizeof (glob_addr)); 
     1005    socklen_t glob_len = 0; 
     1006    unsigned glob_count = 1; 
     1007 
     1008    /* TODO: use iconv and charset attribute instead of EnsureUTF8 */ 
     1009    while (*psz_sdp) 
     1010    { 
     1011        /* Extract one line */ 
     1012        char *eol = strchr (psz_sdp, '\n'); 
     1013        size_t linelen = eol ? (size_t)(eol - psz_sdp) : strlen (psz_sdp); 
     1014        char line[linelen + 1]; 
     1015        memcpy (line, psz_sdp, linelen); 
     1016        line[linelen] = '\0'; 
     1017 
     1018        psz_sdp += linelen + 1; 
     1019 
     1020        /* Remove carriage return if present */ 
     1021        eol = strchr (line, '\r'); 
     1022        if (eol != NULL) 
     1023        { 
     1024            linelen = eol - line; 
     1025            line[linelen] = '\0'; 
     1026        } 
     1027 
     1028        /* Validate line */ 
     1029        char cat = line[0], *data = line + 2; 
     1030        if (!cat || (strchr ("vosiuepcbtrzkam", cat) == NULL)) 
     1031        { 
     1032            /* MUST ignore SDP with unknown line type */ 
     1033            msg_Dbg (p_obj, "unknown SDP line type: 0x%02x", (int)cat); 
     1034            goto error; 
     1035        } 
     1036        if (line[1] != '=') 
     1037        { 
     1038            msg_Dbg (p_obj, "invalid SDP line: %s", line); 
     1039            goto error; 
     1040        } 
     1041 
     1042        assert (linelen >= 2); 
     1043 
     1044        /* SDP parsing state machine 
     1045         * We INTERNALLY use uppercase for session, lowercase for media 
     1046         */ 
     1047        switch (expect) 
     1048        { 
     1049            /* Session description */ 
     1050            case 'V': 
     1051                expect = 'O'; 
     1052                if (cat != 'v') 
     1053                { 
     1054                    msg_Dbg (p_obj, "missing SDP version"); 
     1055                    goto error; 
     1056                } 
     1057                if (strcmp (data, "0")) 
     1058                { 
     1059                    msg_Dbg (p_obj, "unknown SDP version: %s", data); 
     1060                    goto error; 
     1061                } 
    10791062                break; 
    1080             case( 's' ): 
    1081                 p_sdp->psz_sessionname = strdup( &psz_sdp[2] ); 
    1082                 break; 
    1083             case ( 'o' ): 
     1063 
     1064            case 'O': 
    10841065            { 
    1085                 int i_field = 0; 
    1086                 /* o field is <username> <session id> <version> 
    1087                  *  <network type> <address type> <address> */ 
    1088  
    1089 #define GET_FIELD( store ) \ 
    1090                 psz_eof = strchr( psz_parse, ' ' ); \ 
    1091                 if( psz_eof ) \ 
    1092                 { \ 
    1093                     *psz_eof=0; store = strdup( psz_parse ); \ 
    1094                 } \ 
    1095                 else \ 
    1096                 { \ 
    1097                     if( i_field != 5 ) \ 
    1098                     { \ 
    1099                         b_invalid = VLC_TRUE; break; \ 
    1100                     } \ 
    1101                     else \ 
    1102                     { \ 
    1103                         store = strdup( psz_parse ); \ 
    1104                     } \ 
    1105                 }; \ 
    1106                 psz_parse = psz_eof + 1; i_field++; 
    1107  
    1108  
    1109                 psz_parse = (char *)&psz_sdp[2]; 
    1110                 GET_FIELD( p_sdp->psz_username ); 
    1111                 GET_FIELD( psz_sess_id ); 
    1112  
    1113                 p_sdp->i_session_id = atoll( psz_sess_id ); 
    1114  
    1115                 FREENULL( psz_sess_id ); 
    1116  
    1117                 GET_FIELD( psz_sess_id ); 
    1118                 FREENULL( psz_sess_id ); 
    1119  
    1120                 GET_FIELD( p_sdp->psz_network_type ); 
    1121                 GET_FIELD( p_sdp->psz_address_type ); 
    1122                 GET_FIELD( p_sdp->psz_address ); 
    1123  
     1066                expect = 'S'; 
     1067                if (cat != 'o') 
     1068                { 
     1069                    msg_Dbg (p_obj, "missing SDP originator"); 
     1070                    goto error; 
     1071                } 
     1072 
     1073                if ((sscanf (data, "%63s "I64Fu" "I64Fu" IN IP%u %1023s", 
     1074                             p_sdp->username, &p_sdp->session_id, 
     1075                             &p_sdp->session_version, &p_sdp->orig_ip_version, 
     1076                             p_sdp->orig_host) != 5) 
     1077                 || ((p_sdp->orig_ip_version != 4) 
     1078                  && (p_sdp->orig_ip_version != 6))) 
     1079                { 
     1080                    msg_Dbg (p_obj, "SDP origin not supported: %s\n", data); 
     1081                    /* Or maybe out-of-range, but this looks suspicious */ 
     1082                    return NULL; 
     1083                } 
     1084                EnsureUTF8 (p_sdp->orig_host); 
    11241085                break; 
    11251086            } 
    1126             case( 'i' ): 
    1127             case( 'u' ): 
    1128             case( 'e' ): 
    1129             case( 'p' ): 
    1130             case( 't' ): 
    1131             case( 'r' ): 
    1132                 break; 
    1133             case( 'a' ): /* attribute */ 
     1087 
     1088            case 'S': 
    11341089            { 
    1135                 char *psz_eon = strchr( &psz_sdp[2], ':' ); 
    1136                 attribute_t *p_attr = malloc( sizeof( attribute_t ) ); 
    1137  
    1138                 /* Attribute with value */ 
    1139                 if( psz_eon ) 
    1140                 { 
    1141                     *psz_eon++ = '\0'; 
    1142  
    1143                     p_attr->psz_field = strdup( &psz_sdp[2] ); 
    1144                     p_attr->psz_value = strdup( psz_eon ); 
    1145                 } 
    1146                 else /* Attribute without value */ 
    1147                 { 
    1148                     p_attr->psz_field = strdup( &psz_sdp[2] ); 
    1149                     p_attr->psz_value = NULL; 
    1150                 } 
    1151  
    1152                 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr ); 
     1090                expect = 'I'; 
     1091                if ((cat != 's') || !*data) 
     1092                { 
     1093                    /* MUST be present AND non-empty */ 
     1094                    msg_Dbg (p_obj, "missing SDP session name"); 
     1095                    goto error; 
     1096                } 
     1097                assert (p_sdp->psz_sessionname == NULL); // no memleak here 
     1098                p_sdp->psz_sessionname = strdup (data); 
     1099                EnsureUTF8 (p_sdp->psz_sessionname); 
     1100                if (p_sdp->psz_sessionname == NULL) 
     1101                    goto error; 
    11531102                break; 
    11541103            } 
    11551104 
    1156             case( 'm' ): /* Media announcement */ 
     1105            case 'I': 
     1106                expect = 'U'; 
     1107                if (cat == 'i') 
     1108                    break; 
     1109            case 'U': 
     1110                expect = 'E'; 
     1111                if (cat == 'u') 
     1112                    break; 
     1113            case 'E': 
     1114                expect = 'E'; 
     1115                if (cat == 'e') 
     1116                    break; 
     1117            case 'P': 
     1118                expect = 'P'; 
     1119                if (cat == 'p') 
     1120                    break; 
     1121            case 'C': 
     1122                expect = 'B'; 
     1123                if (cat == 'c') 
     1124                { 
     1125                    if (ParseSDPConnection (data, &glob_addr, &glob_len, 
     1126                                            &glob_count)) 
     1127                    { 
     1128                        msg_Dbg (p_obj, "SDP connection infos not supported: " 
     1129                                 "%s", data); 
     1130                        goto error; 
     1131                    } 
     1132                    break; 
     1133                } 
     1134            case 'B': 
     1135                assert (expect == 'B'); 
     1136                if (cat == 'b') 
     1137                    break; 
     1138            case 'T': 
     1139                expect = 'R'; 
     1140                if (cat != 't') 
     1141                { 
     1142                    msg_Dbg (p_obj, "missing SDP time description"); 
     1143                    goto error; 
     1144                } 
     1145                break; 
     1146 
     1147            case 'R': 
     1148                if ((cat == 't') || (cat == 'r')) 
     1149                    break; 
     1150 
     1151            case 'Z': 
     1152                expect = 'K'; 
     1153                if (cat == 'z') 
     1154                    break; 
     1155            case 'K': 
     1156                expect = 'A'; 
     1157                if (cat == 'k') 
     1158                    break; 
     1159            case 'A': 
     1160                //expect = 'A'; 
     1161                if (cat == 'a') 
     1162                { 
     1163                    attribute_t *p_attr = MakeAttribute (data); 
     1164                    TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr ); 
     1165                    break; 
     1166                } 
     1167 
     1168            /* Media description */ 
     1169            case 'm': 
    11571170            { 
    1158                 /* If we have several medias, we pass the announcement to 
    1159                  * LIVE.COM, so just count them */ 
    1160                 p_sdp->i_media++; 
    1161                 if( p_sdp->i_media == 1 ) 
    1162                 { 
    1163                     p_sdp->psz_media = strdup( &psz_sdp[2] ); 
    1164                 } 
     1171                expect = 'i'; 
     1172                if (cat != 'm') 
     1173                { 
     1174                    msg_Dbg (p_obj, "missing SDP media description"); 
     1175                    goto error; 
     1176                } 
     1177                struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac; 
     1178 
     1179                memcpy (&m->addr, &glob_addr, m->addrlen = glob_len); 
     1180                m->n_addr = glob_count; 
     1181 
     1182                /* TODO: remember media type (if we need multiple medias) */ 
     1183                data = strchr (data, ' '); 
     1184                if (data == NULL) 
     1185                { 
     1186                    msg_Dbg (p_obj, "missing SDP media port"); 
     1187                    goto error; 
     1188                } 
     1189                int port = atoi (data); 
     1190                if (port <= 0 || port >= 65536) 
     1191                { 
     1192                    msg_Dbg (p_obj, "invalid transport port %d", port); 
     1193                    goto error; 
     1194                } 
     1195                net_SetPort ((struct sockaddr *)&m->addr, htons (port)); 
     1196 
     1197                data = strchr (data, ' '); 
     1198                if (data == NULL) 
     1199                { 
     1200                    msg_Dbg (p_obj, "missing SDP media format"); 
     1201                    goto error; 
     1202                } 
     1203                m->fmt = strdup (data); 
     1204                if (m->fmt == NULL) 
     1205                    goto error; 
     1206 
     1207                p_sdp->mediac++; 
    11651208                break; 
    11661209            } 
    1167  
    1168             case( 'c' ): 
    1169             { 
    1170                 if( p_sdp->psz_connection != NULL ) // FIXME 
     1210            case 'i': 
     1211                expect = 'c'; 
     1212                if (cat == 'i') 
    11711213                    break; 
    1172  
    1173                 p_sdp->psz_connection = strdup( &psz_sdp[2] ); 
     1214            case 'c': 
     1215                expect = 'b'; 
     1216                if (cat == 'c') 
     1217                { 
     1218                    struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac; 
     1219                    if (ParseSDPConnection (data, &m->addr, &m->addrlen, 
     1220                                            &m->n_addr)) 
     1221                    { 
     1222                        msg_Dbg (p_obj, "SDP connection infos not supported: " 
     1223                                 "%s", data); 
     1224                        goto error; 
     1225                    } 
     1226                    break; 
     1227                } 
     1228            case 'b': 
     1229                expect = 'b'; 
     1230                if (cat == 'b') 
     1231                    break; 
     1232            case 'k': 
     1233                expect = 'a'; 
     1234                if (cat == 'k') 
     1235                    break; 
     1236            case 'a': 
     1237                assert (expect == 'a'); 
     1238                if (cat == 'a') 
     1239                { 
     1240                    attribute_t *p_attr = MakeAttribute (data); 
     1241                    if (p_attr == NULL) 
     1242                        goto error; 
     1243 
     1244                    TAB_APPEND (p_sdp->mediav[p_sdp->mediac - 1].i_attributes, 
     1245                                p_sdp->mediav[p_sdp->mediac - 1].pp_attributes, p_attr); 
     1246                    break; 
     1247                } 
     1248 
     1249                if (cat == 'm') 
     1250                { 
     1251                    /* TODO */ 
     1252                    msg_Dbg (p_obj, "multi-media SDP not implemented -> live555"); 
     1253                    goto error; 
     1254                } 
     1255 
     1256                if (cat != 'm') 
     1257                { 
     1258                    msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat); 
     1259                    goto error; 
     1260                } 
    11741261                break; 
    1175             } 
    11761262 
    11771263            default: 
    1178                break; 
    1179         } 
    1180  
    1181         if( b_invalid ) 
    1182         { 
    1183             FreeSDP( p_sdp ); p_sdp = NULL; 
    1184             return NULL; 
    1185         } 
    1186  
    1187         psz_sdp = psz_eol; 
     1264                msg_Err (p_obj, "*** BUG in SDP parser! ***"); 
     1265                goto error; 
     1266        } 
    11881267    } 
    11891268 
    11901269    return p_sdp; 
     1270 
     1271error: 
     1272    FreeSDP (p_sdp); 
     1273    return NULL; 
    11911274} 
    11921275 
     
    12541337static void FreeSDP( sdp_t *p_sdp ) 
    12551338{ 
    1256     int i; 
    12571339    FREENULL( p_sdp->psz_sdp ); 
    12581340    FREENULL( p_sdp->psz_sessionname ); 
    1259     FREENULL( p_sdp->psz_connection ); 
    1260     FREENULL( p_sdp->psz_media ); 
    12611341    FREENULL( p_sdp->psz_uri ); 
    1262     FREENULL( p_sdp->psz_username ); 
    1263     FREENULL( p_sdp->psz_network_type ); 
    1264  
    1265     FREENULL( p_sdp->psz_address ); 
    1266     FREENULL( p_sdp->psz_address_type ); 
    1267  
    1268     for( i= p_sdp->i_attributes - 1; i >= 0 ; i-- ) 
    1269     { 
    1270         struct attribute_t *p_attr = p_sdp->pp_attributes[i]; 
    1271         FREENULL( p_sdp->pp_attributes[i]->psz_field ); 
    1272         FREENULL( p_sdp->pp_attributes[i]->psz_value ); 
    1273         REMOVE_ELEM( p_sdp->pp_attributes, p_sdp->i_attributes, i); 
    1274         FREENULL( p_attr ); 
    1275     } 
    1276     FREENULL( p_sdp ); 
     1342 
     1343    for (unsigned j = 0; j < p_sdp->mediac; j++) 
     1344    { 
     1345        free (p_sdp->mediav[j].fmt); 
     1346        for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++) 
     1347            FreeAttribute (p_sdp->mediav[j].pp_attributes[i]); 
     1348    } 
     1349 
     1350    for (int i = 0; i < p_sdp->i_attributes; i++) 
     1351        FreeAttribute (p_sdp->pp_attributes[i]); 
     1352 
     1353    free (p_sdp->pp_attributes); 
     1354    free (p_sdp); 
    12771355} 
    12781356 
     
    13091387{ 
    13101388    /* A session is identified by 
    1311      * username, session_id, network type, address type and address */ 
    1312     if( p_sdp1->psz_username && p_sdp2->psz_username && 
    1313         p_sdp1->psz_network_type && p_sdp2->psz_network_type && 
    1314         p_sdp1->psz_address_type && p_sdp2->psz_address_type && 
    1315         p_sdp1->psz_address &&  p_sdp2->psz_address ) 
    1316     { 
    1317         if(!strcmp( p_sdp1->psz_username , p_sdp2->psz_username ) && 
    1318            !strcmp( p_sdp1->psz_network_type , p_sdp2->psz_network_type ) && 
    1319            !strcmp( p_sdp1->psz_address_type , p_sdp2->psz_address_type ) && 
    1320            !strcmp( p_sdp1->psz_address , p_sdp2->psz_address ) && 
    1321            p_sdp1->i_session_id == p_sdp2->i_session_id ) 
    1322         { 
    1323             return VLC_TRUE; 
    1324         } 
    1325         else 
    1326         { 
    1327             return VLC_FALSE; 
    1328         } 
     1389     * - username, 
     1390     * - session_id, 
     1391     * - network type (which is always IN), 
     1392     * - address type (currently, this means IP version), 
     1393     * - and hostname. 
     1394     */ 
     1395    if (strcmp (p_sdp1->username, p_sdp2->username) 
     1396     || (p_sdp1->session_id != p_sdp2->session_id) 
     1397     || (p_sdp1->orig_ip_version != p_sdp2->orig_ip_version) 
     1398     || strcmp (p_sdp1->orig_host, p_sdp2->orig_host)) 
     1399        return VLC_FALSE; 
     1400 
     1401    return VLC_TRUE; 
     1402
     1403 
     1404 
     1405static inline attribute_t *MakeAttribute (const char *str) 
     1406
     1407    attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1); 
     1408    if (a == NULL) 
     1409        return NULL; 
     1410 
     1411    strcpy (a->name, str); 
     1412    EnsureUTF8 (a->name); 
     1413    char *value = strchr (a->name, ':'); 
     1414    if (value != NULL) 
     1415    { 
     1416        *value++ = '\0'; 
     1417        a->value = value; 
    13291418    } 
    13301419    else 
    1331     { 
    1332         return VLC_FALSE; 
    1333     } 
    1334 
     1420        a->value = ""; 
     1421    return a; 
     1422
     1423 
     1424 
     1425static const char *GetAttribute (attribute_t **tab, unsigned n, 
     1426                                 const char *name) 
     1427
     1428    for (unsigned i = 0; i < n; i++) 
     1429        if (strcasecmp (tab[i]->name, name) == 0) 
     1430