X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fservices_discovery%2Fsap.c;h=23b8c946238a85387737fb9b505ce951f5f1d5a7;hb=6ee1e193fd896ab9a4729fde14f009d9ce629815;hp=03666e5a55ac1c05ca30d2f48fe3ac35b7354103;hpb=792ed4d67f964ba5c2aa1b634260cae0fe91ada3;p=vlc diff --git a/modules/services_discovery/sap.c b/modules/services_discovery/sap.c index 03666e5a55..23b8c94623 100644 --- a/modules/services_discovery/sap.c +++ b/modules/services_discovery/sap.c @@ -26,10 +26,9 @@ /***************************************************************************** * Includes *****************************************************************************/ -#define _GNU_SOURCE -#include /* malloc(), free() */ - #include +#include + #include #include @@ -50,6 +49,10 @@ # include #endif +#ifndef WIN32 +# include +#endif + /************************************************************************ * Macros and definitions ************************************************************************/ @@ -68,14 +71,6 @@ #define SAP_V4_LINK_ADDRESS "224.0.0.255" #define ADD_SESSION 1 -#define SAP_V6_1 "FF0" -/* Scope is inserted between them */ -#define SAP_V6_2 "::2:7FFE" -/* See RFC3513 for list of valid scopes */ -/* FIXME: find a way to listen to link-local scope */ -static const char ipv6_scopes[] = "1456789ABCDE"; - - /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -85,12 +80,10 @@ static const char ipv6_scopes[] = "1456789ABCDE"; "can specify a specific address." ) #define SAP_IPV4_TEXT N_( "IPv4 SAP" ) #define SAP_IPV4_LONGTEXT N_( \ - "Listen to IPv4 announcements " \ - "on the standard address." ) + "Listen to IPv4 announcements on the standard addresses." ) #define SAP_IPV6_TEXT N_( "IPv6 SAP" ) #define SAP_IPV6_LONGTEXT N_( \ - "Listen to IPv6 announcements " \ - "on the standard addresses." ) + "Listen to IPv6 announcements on the standard addresses." ) #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" ) #define SAP_SCOPE_LONGTEXT N_( \ "Scope for IPv6 announcements (default is 8)." ) @@ -151,7 +144,7 @@ vlc_module_begin(); set_callbacks( Open, Close ); add_submodule(); - set_description( _("SDP file parser for UDP") ); + set_description( _("SDP Descriptions parser") ); add_shortcut( "sdp" ); set_capability( "demux2", 51 ); set_callbacks( OpenDemux, CloseDemux ); @@ -205,7 +198,7 @@ struct sdp_t /* medias (well, we only support one atm) */ unsigned mediac; - struct sdp_media_t mediav[1]; + struct sdp_media_t *mediav; }; struct attribute_t @@ -225,8 +218,6 @@ struct sap_announce_t sdp_t *p_sdp; int i_input_id; - int i_item_id_cat; - int i_item_id_one; }; struct services_discovery_sys_t @@ -235,10 +226,6 @@ struct services_discovery_sys_t int i_fd; int *pi_fd; - /* playlist node */ - playlist_item_t *p_node_cat; - playlist_item_t *p_node_one; - /* Table of announces */ int i_announces; struct sap_announce_t **pp_announces; @@ -277,6 +264,8 @@ struct demux_sys_t static inline attribute_t *MakeAttribute (const char *str); static const char *GetAttribute (attribute_t **tab, unsigned n, const char *name); static inline void FreeAttribute (attribute_t *a); + static const char *FindAttribute (const sdp_t *sdp, unsigned media, + const char *name); static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 ); static int InitSocket( services_discovery_t *p_sd, const char *psz_address, int i_port ); @@ -311,16 +300,11 @@ static int Open( vlc_object_t *p_this ) #endif /* Cache sap_timeshift value */ - p_sys->b_timeshift = var_CreateGetInteger( p_sd, "sap-timeshift" ) - ? VLC_TRUE : VLC_FALSE; + p_sys->b_timeshift = var_CreateGetInteger( p_sd, "sap-timeshift" ); - /* Create our playlist node */ - pl_Yield( p_sd ); + /* Set our name */ + services_discovery_SetLocalizedName( p_sd, _("SAP") ); - playlist_NodesPairCreate( pl_Get( p_sd ), _("SAP sessions"), - &p_sys->p_node_cat, &p_sys->p_node_one, - VLC_TRUE ); - p_sys->p_node_cat->p_input->b_prefers_tree = VLC_TRUE; p_sys->i_announces = 0; p_sys->pp_announces = NULL; @@ -333,11 +317,11 @@ static int Open( vlc_object_t *p_this ) static int OpenDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t *)p_this; - uint8_t *p_peek; - int i_max_sdp = 1024; - int i_sdp = 0; + const uint8_t *p_peek; char *psz_sdp = NULL; sdp_t *p_sdp = NULL; + int errval = VLC_EGENERIC; + size_t i_len; if( !var_CreateGetInteger( p_demux, "sap-parse" ) ) { @@ -345,44 +329,40 @@ static int OpenDemux( vlc_object_t *p_this ) return VLC_EGENERIC; } - /* Probe for SDP */ - if( p_demux->s ) - { - if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC; + assert( p_demux->s ); /* this is NOT an access_demux */ - if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) && - strncmp( (char*)p_peek, "v=0\n", 4 ) && - ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) ) - { - return VLC_EGENERIC; - } - } + /* Probe for SDP */ + if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) + return VLC_EGENERIC; - psz_sdp = (char *)malloc( i_max_sdp ); - if( !psz_sdp ) return VLC_EGENERIC; + if( memcmp( p_peek, "v=0\r\no=", 7 ) && memcmp( p_peek, "v=0\no=", 6 ) ) + return VLC_EGENERIC; /* Gather the complete sdp file */ - for( ;; ) + for( i_len = 0, psz_sdp = NULL; i_len < 65536; ) { - int i_read = stream_Read( p_demux->s, - &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 ); - - if( i_read < 0 ) + const int i_read_max = 1024; + char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max ); + size_t i_read; + if( psz_sdp_new == NULL ) { - msg_Err( p_demux, "failed to read SDP" ); + errval = VLC_ENOMEM; goto error; } + psz_sdp = psz_sdp_new; - i_sdp += i_read; - - if( i_read < i_max_sdp - i_sdp - 1 ) + i_read = stream_Read( p_demux->s, &psz_sdp[i_len], i_read_max ); + if( i_read < 0 ) { - psz_sdp[i_sdp] = '\0'; - break; + msg_Err( p_demux, "cannot read SDP" ); + goto error; } + i_len += i_read; - i_max_sdp += 1000; - psz_sdp = (char *)realloc( psz_sdp, i_max_sdp ); + psz_sdp[i_len] = '\0'; + + if( i_read < i_read_max ) + break; // EOF } p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp ); @@ -415,7 +395,7 @@ error: FREENULL( psz_sdp ); if( p_sdp ) FreeSDP( p_sdp ); p_sdp = NULL; stream_Seek( p_demux->s, 0 ); - return VLC_EGENERIC; + return errval; } /***************************************************************************** @@ -447,10 +427,6 @@ static void Close( vlc_object_t *p_this ) } FREENULL( p_sys->pp_announces ); - playlist_NodeDelete( pl_Get(p_sd), p_sys->p_node_cat, VLC_TRUE, - VLC_TRUE ); - playlist_NodeDelete( pl_Get(p_sd), p_sys->p_node_one, VLC_TRUE, - VLC_TRUE ); pl_Release( p_sd ); free( p_sys ); } @@ -495,12 +471,50 @@ static void Run( services_discovery_t *p_sd ) } if( var_CreateGetInteger( p_sd, "sap-ipv6" ) ) { - char psz_address[] = SAP_V6_1"0"SAP_V6_2; - const char *c_scope; + char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%"; - for( c_scope = ipv6_scopes; *c_scope; c_scope++ ) +#ifndef WIN32 + struct if_nameindex *l = if_nameindex (); + if (l != NULL) { - psz_address[sizeof(SAP_V6_1) - 1] = *c_scope; + char *ptr = strchr (psz_address, '%') + 1; + for (unsigned i = 0; l[i].if_index; i++) + { + strcpy (ptr, l[i].if_name); + InitSocket (p_sd, psz_address, SAP_PORT); + } + if_freenameindex (l); + } +#else + /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks, + which if_nameindex uses internally anyway */ + + // first create a dummy socket to pin down the protocol family + SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if( s != INVALID_SOCKET ) + { + INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces + size_t len = sizeof(ifaces); + + if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) ) + { + unsigned ifcount = len/sizeof(INTERFACE_INFO); + char *ptr = strchr (psz_address, '%') + 1; + for(unsigned i = 1; i<=ifcount; ++i ) + { + // append link-local zone identifier + sprintf(ptr, "%d", i); + } + } + closesocket(s); + } +#endif + *strchr (psz_address, '%') = '\0'; + + static const char ipv6_scopes[] = "1456789ABCDE"; + for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++) + { + psz_address[3] = *c_scope; InitSocket( p_sd, psz_address, SAP_PORT ); } } @@ -526,7 +540,7 @@ static void Run( services_discovery_t *p_sd ) i_read = net_Select( p_sd, p_sd->p_sys->pi_fd, p_sd->p_sys->i_fd, p_buffer, - MAX_SAP_BUFFER, 500000 ); + MAX_SAP_BUFFER ); /* Check for items that need deletion */ for( i = 0; i < p_sd->p_sys->i_announces; i++ ) @@ -576,13 +590,13 @@ static int Demux( demux_t *p_demux ) return VLC_EGENERIC; } - p_parent_input = input_GetItem(p_input); + p_parent_input = input_GetItem( p_input ); + + input_item_SetURI( p_parent_input, p_sdp->psz_uri ); + input_item_SetName( p_parent_input, p_sdp->psz_sessionname ); vlc_mutex_lock( &p_parent_input->lock ); - FREENULL( p_parent_input->psz_uri ); - p_parent_input->psz_uri = strdup( p_sdp->psz_uri ); - FREENULL( p_parent_input->psz_name ); - p_parent_input->psz_name = strdup( p_sdp->psz_sessionname ); + p_parent_input->i_type = ITEM_TYPE_NET; if( p_playlist->status.p_item && @@ -751,7 +765,6 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash, sdp_t *p_sdp ) { input_item_t *p_input; - playlist_item_t *p_item, *p_child; const char *psz_value; sap_announce_t *p_sap = (sap_announce_t *)malloc( sizeof(sap_announce_t ) ); @@ -783,46 +796,22 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash, psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "tool" ); if( psz_value != NULL ) { - input_ItemAddInfo( p_input, _("Session"),_("Tool"), psz_value ); + input_ItemAddInfo( p_input, _("Session"), _("Tool"), "%s", + psz_value ); } if( strcmp( p_sdp->username, "-" ) ) { - input_ItemAddInfo( p_input, _("Session"), - _("User"), p_sdp->username ); + input_ItemAddInfo( p_input, _("Session"), _("User"), "%s", + p_sdp->username ); } /* Handle group */ - psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "x-plgroup" ); - if( psz_value == NULL ) - psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "plgroup" ); - - if( psz_value != NULL ) - { - p_child = playlist_ChildSearchName( p_sys->p_node_cat, psz_value ); - - if( p_child == NULL ) - { - p_child = playlist_NodeCreate( pl_Get( p_sd ), psz_value, - p_sys->p_node_cat, 0 ); - p_child->i_flags &= ~PLAYLIST_SKIP_FLAG; - } - } + if (p_sap->p_sdp->mediac >= 1) + psz_value = FindAttribute (p_sap->p_sdp, 0, "x-plgroup"); else - { - p_child = p_sys->p_node_cat; - } - - p_item = playlist_NodeAddInput( pl_Get( p_sd ), p_input, p_child, - PLAYLIST_APPEND, PLAYLIST_END ); - p_item->i_flags &= ~PLAYLIST_SKIP_FLAG; - p_item->i_flags &= ~PLAYLIST_SAVE_FLAG; - p_sap->i_item_id_cat = p_item->i_id; + psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "x-plgroup" ); - p_item = playlist_NodeAddInput( pl_Get( p_sd ), p_input, - p_sys->p_node_one, PLAYLIST_APPEND, PLAYLIST_END ); - p_item->i_flags &= ~PLAYLIST_SKIP_FLAG; - p_item->i_flags &= ~PLAYLIST_SAVE_FLAG; - p_sap->i_item_id_one = p_item->i_id; + services_discovery_AddItem( p_sd, p_input, psz_value /* category name */ ); TAB_APPEND( p_sys->i_announces, p_sys->pp_announces, p_sap ); @@ -843,16 +832,37 @@ static const char *FindAttribute (const sdp_t *sdp, unsigned media, /* Fill p_sdp->psz_uri */ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) { - if (p_sdp->mediac != 1) + if (p_sdp->mediac == 0) + { + msg_Dbg (p_obj, "Ignoring SDP with no media"); + return VLC_EGENERIC; + } + + for (unsigned i = 1; i < p_sdp->mediac; i++) + { + if ((p_sdp->mediav[i].n_addr != p_sdp->mediav->n_addr) + || (p_sdp->mediav[i].addrlen != p_sdp->mediav->addrlen) + || memcmp (&p_sdp->mediav[i].addr, &p_sdp->mediav->addr, + p_sdp->mediav->addrlen)) + { + msg_Dbg (p_obj, "Multiple media ports not supported -> live555"); + return VLC_EGENERIC; + } + } + + if (p_sdp->mediav->n_addr != 1) + { + msg_Dbg (p_obj, "Layered encoding not supported -> live555"); return VLC_EGENERIC; + } char psz_uri[1026]; const char *host; int port; psz_uri[0] = '['; - if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav[0].addr), - p_sdp->mediav[0].addrlen, psz_uri + 1, + if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav->addr), + p_sdp->mediav->addrlen, psz_uri + 1, sizeof (psz_uri) - 2, &port, NI_NUMERICHOST)) return VLC_EGENERIC; @@ -870,7 +880,7 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) return VLC_ENOMEM; char *subtype = strchr (sdp_proto, ' '); - if (sdp_proto == NULL) + if (subtype == NULL) { msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto); p_sdp->i_media_type = 0; @@ -894,26 +904,13 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) "\0"; const char *vlc_proto = NULL; + uint8_t flags = 0; for (const char *proto = proto_match; *proto;) { - if (strcasecmp (proto, sdp_proto)) + if (strcasecmp (proto, sdp_proto) == 0) { vlc_proto = proto + strlen (proto) + 1; - - if (vlc_proto[strlen (vlc_proto) + 1] & 1) - { - /* COMEDIA check */ - const char *setup = FindAttribute (p_sdp, 0, "setup"); - if (setup == NULL) - setup = "active"; /* default value */ - - if (strcmp (setup, "actpass") && strcmp (setup, "passive")) - { - msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup); - free (sdp_proto); - return VLC_EGENERIC; - } - } + flags = vlc_proto[strlen (vlc_proto) + 1]; break; } proto += strlen (proto) + 1; @@ -928,24 +925,72 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) return VLC_EGENERIC; } - /* handle SSM case */ - const char *sfilter = FindAttribute (p_sdp, 0, "source-filter"); - char psz_source[258] = ""; - if (sfilter != NULL) + if (flags & 1) { - char psz_source_ip[256]; + /* Connection-oriented media */ + const char *setup = FindAttribute (p_sdp, 0, "setup"); + if (setup == NULL) + setup = "active"; /* default value */ - if (sscanf (sfilter, " incl IN IP%*c %*s %255s ", psz_source_ip) == 1) + if (strcmp (setup, "actpass") && strcmp (setup, "passive")) { - if (strchr (psz_source_ip, ':') != NULL) - sprintf (psz_source, "[%s]", psz_source_ip); - else - strcpy (psz_source, psz_source_ip); + msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup); + return VLC_EGENERIC; } + + if (asprintf (&p_sdp->psz_uri, "%s://%s:%d", vlc_proto, + host, port) == -1) + return VLC_ENOMEM; } + else + { + /* Non-connected (normally multicast) media */ + + char psz_source[258] = ""; + const char *sfilter = FindAttribute (p_sdp, 0, "source-filter"); + if (sfilter != NULL) + { + char psz_source_ip[256]; + unsigned ipv; + + if (sscanf (sfilter, " incl IN IP%u %*s %255s ", &ipv, + psz_source_ip) == 2) + { + /* According to RFC4570, FQDNs can be used for source-filters, + * but -seriously- this is impractical */ + switch (ipv) + { +#ifdef AF_INET6 + case 6: + { + struct in6_addr addr; + if ((inet_pton (AF_INET6, psz_source_ip, &addr) > 0) + && (inet_ntop (AF_INET6, &addr, psz_source + 1, + sizeof (psz_source) - 2) != NULL)) + { + psz_source[0] = '['; + psz_source[strlen (psz_source)] = ']'; + } + break; + } +#endif + case 4: + { + struct in_addr addr; + if ((inet_pton (AF_INET, psz_source_ip, &addr) > 0) + && (inet_ntop (AF_INET, &addr, psz_source, + sizeof (psz_source)) == NULL)) + *psz_source = '\0'; + break; + } + } + } + } - asprintf( &p_sdp->psz_uri, "%s://%s@%s:%i", vlc_proto, psz_source, - host, port ); + if (asprintf (&p_sdp->psz_uri, "%s://%s@%s:%i", vlc_proto, psz_source, + host, port) == -1) + return VLC_ENOMEM; + } return VLC_SUCCESS; } @@ -1019,6 +1064,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) memset (&glob_addr, 0, sizeof (glob_addr)); socklen_t glob_len = 0; unsigned glob_count = 1; + int port = 0; /* TODO: use iconv and charset attribute instead of EnsureUTF8 */ while (*psz_sdp) @@ -1182,6 +1228,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) /* Media description */ case 'm': + media: { expect = 'i'; if (cat != 'm') @@ -1189,8 +1236,16 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) msg_Dbg (p_obj, "missing SDP media description"); goto error; } - struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac; + struct sdp_media_t *m; + m = realloc (p_sdp->mediav, (p_sdp->mediac + 1) * sizeof (*m)); + if (m == NULL) + goto error; + p_sdp->mediav = m; + m += p_sdp->mediac; + p_sdp->mediac++; + + memset (m, 0, sizeof (*m)); memcpy (&m->addr, &glob_addr, m->addrlen = glob_len); m->n_addr = glob_count; @@ -1201,7 +1256,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) msg_Dbg (p_obj, "missing SDP media port"); goto error; } - int port = atoi (++data); + port = atoi (++data); if (port <= 0 || port >= 65536) { msg_Dbg (p_obj, "invalid transport port %d", port); @@ -1219,7 +1274,6 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) if (m->fmt == NULL) goto error; - p_sdp->mediac++; break; } case 'i': @@ -1230,7 +1284,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) expect = 'b'; if (cat == 'c') { - struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac; + struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac - 1; if (ParseSDPConnection (data, &m->addr, &m->addrlen, &m->n_addr)) { @@ -1238,6 +1292,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) "%s", data); goto error; } + net_SetPort ((struct sockaddr *)&m->addr, htons (port)); break; } case 'b': @@ -1262,11 +1317,7 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) } if (cat == 'm') - { - /* TODO */ - msg_Dbg (p_obj, "multi-media SDP not implemented -> live555"); - goto error; - } + goto media; if (cat != 'm') { @@ -1295,7 +1346,7 @@ static int InitSocket( services_discovery_t *p_sd, const char *psz_address, if (i_fd == -1) return VLC_EGENERIC; - net_StopSend( i_fd ); + shutdown( i_fd, SHUT_WR ); INSERT_ELEM (p_sd->p_sys->pi_fd, p_sd->p_sys->i_fd, p_sd->p_sys->i_fd, i_fd); return VLC_SUCCESS; @@ -1359,7 +1410,9 @@ static void FreeSDP( sdp_t *p_sdp ) free (p_sdp->mediav[j].fmt); for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++) FreeAttribute (p_sdp->mediav[j].pp_attributes[i]); + free (p_sdp->mediav[j].pp_attributes); } + free (p_sdp->mediav); for (int i = 0; i < p_sdp->i_attributes; i++) FreeAttribute (p_sdp->pp_attributes[i]);