Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
GSoC
GSoC2018
macOS
vlc
Commits
14fb31d9
Commit
14fb31d9
authored
Feb 11, 2007
by
Rémi Denis-Courmont
Browse files
Improve SAP parser
parent
60a679f5
Changes
1
Hide whitespace changes
Inline
Side-by-side
modules/services_discovery/sap.c
View file @
14fb31d9
...
...
@@ -2,9 +2,11 @@
* sap.c : SAP interface module
*****************************************************************************
* Copyright (C) 2004-2005 the VideoLAN team
* Copyright © 2007 Rémi Denis-Courmont
* $Id$
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
...
...
@@ -164,41 +166,52 @@ typedef struct sdp_t sdp_t;
typedef
struct
attribute_t
attribute_t
;
typedef
struct
sap_announce_t
sap_announce_t
;
struct
sdp_media_t
{
struct
sdp_t
*
parent
;
char
*
fmt
;
struct
sockaddr_storage
addr
;
socklen_t
addrlen
;
unsigned
n_addr
;
int
i_attributes
;
attribute_t
**
pp_attributes
;
};
/* The structure that contains sdp information */
struct
sdp_t
{
char
*
psz_sdp
;
/* o field */
char
username
[
64
];
uint64_t
session_id
;
uint64_t
session_version
;
unsigned
orig_ip_version
;
char
orig_host
[
1024
];
/* s= field */
char
*
psz_sessionname
;
/* Raw m= and c= fields */
char
*
psz_connection
;
char
*
psz_media
;
/* o field */
char
*
psz_username
;
char
*
psz_network_type
;
char
*
psz_address_type
;
char
*
psz_address
;
int64_t
i_session_id
;
/* old cruft */
/* "computed" URI */
char
*
psz_uri
;
int
i_in
;
/* IP version */
int
i_media
;
int
i_media_type
;
/* a= global attributes */
int
i_attributes
;
attribute_t
**
pp_attributes
;
/* medias (well, we only support one atm) */
unsigned
mediac
;
struct
sdp_media_t
mediav
[
1
];
};
struct
attribute_t
{
c
har
*
psz_field
;
char
*
psz_value
;
c
onst
char
*
value
;
char
name
[
0
]
;
};
struct
sap_announce_t
...
...
@@ -261,7 +274,10 @@ struct demux_sys_t
static
int
RemoveAnnounce
(
services_discovery_t
*
p_sd
,
sap_announce_t
*
p_announce
);
/* Helper functions */
static
char
*
GetAttribute
(
sdp_t
*
p_sdp
,
const
char
*
psz_search
);
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
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
);
static
int
Decompress
(
const
unsigned
char
*
psz_src
,
unsigned
char
**
_dst
,
int
i_len
);
...
...
@@ -377,7 +393,7 @@ static int OpenDemux( vlc_object_t *p_this )
goto
error
;
}
if
(
p_sdp
->
i_
media
>
1
)
if
(
p_sdp
->
media
c
>
1
)
{
goto
error
;
}
...
...
@@ -571,7 +587,7 @@ static int Demux( demux_t *p_demux )
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
(
EnsureUTF8
(
p_sdp
->
psz_sessionname
)
);
p_parent_input
->
psz_name
=
strdup
(
p_sdp
->
psz_sessionname
);
p_parent_input
->
i_type
=
ITEM_TYPE_NET
;
if
(
p_playlist
->
status
.
p_item
&&
...
...
@@ -702,7 +718,7 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
}
/* Multi-media or no-parse -> pass to LIVE.COM */
if
(
p_sdp
->
i_
media
>
1
||
(
p_sdp
->
i_media_type
!=
14
&&
if
(
p_sdp
->
media
c
>
1
||
(
p_sdp
->
i_media_type
!=
14
&&
p_sdp
->
i_media_type
!=
32
&&
p_sdp
->
i_media_type
!=
33
)
||
p_sd
->
p_sys
->
b_parse
==
VLC_FALSE
)
...
...
@@ -731,11 +747,6 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
return
VLC_SUCCESS
;
}
}
/* Add item */
if
(
p_sdp
->
i_media
>
1
)
{
msg_Dbg
(
p_sd
,
"passing to liveMedia"
);
}
CreateAnnounce
(
p_sd
,
i_hash
,
p_sdp
);
...
...
@@ -748,7 +759,7 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
{
input_item_t
*
p_input
;
playlist_item_t
*
p_item
,
*
p_child
;
char
*
psz_value
;
const
char
*
psz_value
;
sap_announce_t
*
p_sap
=
(
sap_announce_t
*
)
malloc
(
sizeof
(
sap_announce_t
)
);
services_discovery_sys_t
*
p_sys
;
...
...
@@ -757,7 +768,6 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
p_sys
=
p_sd
->
p_sys
;
EnsureUTF8
(
p_sdp
->
psz_sessionname
);
p_sap
->
i_last
=
mdate
();
p_sap
->
i_hash
=
i_hash
;
p_sap
->
p_sdp
=
p_sdp
;
...
...
@@ -777,26 +787,24 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
if
(
p_sys
->
b_timeshift
)
input_ItemAddOption
(
p_input
,
":access-filter=timeshift"
);
psz_value
=
GetAttribute
(
p_sap
->
p_sdp
,
"tool"
);
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
);
}
if
(
strcmp
(
p_sdp
->
psz_
username
,
"-"
)
)
if
(
strcmp
(
p_sdp
->
username
,
"-"
)
)
{
input_ItemAddInfo
(
p_input
,
_
(
"Session"
),
_
(
"User"
),
p_sdp
->
psz_
username
);
_
(
"User"
),
p_sdp
->
username
);
}
/* Handle group */
psz_value
=
GetAttribute
(
p_sap
->
p_sdp
,
"x-plgroup"
);
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
,
"plgroup"
);
psz_value
=
GetAttribute
(
p_sap
->
p_sdp
->
pp_attributes
,
p_sap
->
p_sdp
->
i_attributes
,
"plgroup"
);
if
(
psz_value
!=
NULL
)
{
EnsureUTF8
(
psz_value
);
p_child
=
playlist_ChildSearchName
(
p_sys
->
p_node_cat
,
psz_value
);
if
(
p_child
==
NULL
)
...
...
@@ -828,163 +836,91 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
return
p_sap
;
}
static
char
*
GetAttribute
(
sdp_t
*
p_sdp
,
const
char
*
psz_search
)
{
int
i
;
for
(
i
=
0
;
i
<
p_sdp
->
i_attributes
;
i
++
)
{
if
(
!
strncmp
(
p_sdp
->
pp_attributes
[
i
]
->
psz_field
,
psz_search
,
strlen
(
p_sdp
->
pp_attributes
[
i
]
->
psz_field
)
)
)
{
return
p_sdp
->
pp_attributes
[
i
]
->
psz_value
;
}
}
return
NULL
;
}
/* Fill p_sdp->psz_uri */
static
int
ParseConnection
(
vlc_object_t
*
p_obj
,
sdp_t
*
p_sdp
)
{
char
*
psz_eof
=
NULL
;
char
*
psz_parse
=
NULL
;
char
psz_uri
[
1026
];
char
*
psz_proto
=
NULL
;
int
i_port
=
0
;
/* Parse c= field */
if
(
p_sdp
->
psz_connection
)
{
char
hostname
[
1024
];
int
ipv
;
/*
* NOTE: we ignore the TTL parameter on-purpose, as some SAP
* advertisers don't include it (and it is utterly useless).
*/
if
(
sscanf
(
p_sdp
->
psz_connection
,
"IN IP%d %1023[^/]"
,
&
ipv
,
hostname
)
!=
2
)
{
msg_Warn
(
p_obj
,
"unable to parse c field:
\"
%s
\"
"
,
p_sdp
->
psz_connection
);
return
VLC_EGENERIC
;
}
if
(
p_sdp
->
mediac
!=
1
)
return
VLC_EGENERIC
;
switch
(
ipv
)
{
case
4
:
case
6
:
break
;
char
psz_uri
[
1026
];
const
char
*
host
;
int
port
;
default:
msg_Warn
(
p_obj
,
"unknown IP version %d"
,
ipv
);
return
VLC_EGENERIC
;
}
psz_uri
[
0
]
=
'['
;
if
(
vlc_getnameinfo
((
struct
sockaddr
*
)
&
(
p_sdp
->
mediav
[
0
].
addr
),
p_sdp
->
mediav
[
0
].
addrlen
,
psz_uri
+
1
,
sizeof
(
psz_uri
)
-
2
,
&
port
,
NI_NUMERICHOST
))
return
VLC_EGENERIC
;
if
(
strchr
(
hostname
,
':'
)
!=
NULL
)
sprintf
(
psz_uri
,
"[%s]"
,
hostname
);
else
strcpy
(
psz_uri
,
hostname
)
;
if
(
strchr
(
psz_uri
+
1
,
':'
)
)
{
host
=
psz_uri
;
psz_uri
[
strlen
(
psz_uri
)]
=
']'
;
}
else
host
=
psz_uri
+
1
;
/* Parse m= field */
if
(
p_sdp
->
psz_media
)
{
psz_parse
=
p_sdp
->
psz_media
;
psz_eof
=
strchr
(
psz_parse
,
' '
);
char
*
sdp_proto
=
strdup
(
p_sdp
->
mediav
[
0
].
fmt
);
if
(
sdp_proto
==
NULL
)
return
VLC_ENOMEM
;
if
(
psz_eof
)
{
*
psz_eof
=
'\0'
;
/*
* That's ugly. We should go through every media, and make sure
* at least one of them is audio or video. In the mean time, I
* need to accept data too.
*/
if
(
strncmp
(
psz_parse
,
"audio"
,
5
)
&&
strncmp
(
psz_parse
,
"video"
,
5
)
&&
strncmp
(
psz_parse
,
"data"
,
4
)
)
{
msg_Warn
(
p_obj
,
"unhandled media type
\"
%s
\"
"
,
psz_parse
);
return
VLC_EGENERIC
;
}
psz_parse
=
psz_eof
+
1
;
}
else
{
msg_Warn
(
p_obj
,
"unable to parse m field (1)"
);
return
VLC_EGENERIC
;
}
psz_eof
=
strchr
(
psz_parse
,
' '
);
if
(
psz_eof
)
{
*
psz_eof
=
'\0'
;
/* FIXME : multiple port ! */
i_port
=
atoi
(
psz_parse
);
if
(
i_port
<=
0
||
i_port
>=
65536
)
{
msg_Warn
(
p_obj
,
"invalid transport port %i"
,
i_port
);
}
psz_parse
=
psz_eof
+
1
;
}
else
{
msg_Warn
(
p_obj
,
"unable to parse m field (2)"
);
return
VLC_EGENERIC
;
}
psz_eof
=
strchr
(
psz_parse
,
' '
);
if
(
psz_eof
)
{
*
psz_eof
=
'\0'
;
psz_proto
=
strdup
(
psz_parse
);
psz_parse
=
psz_eof
+
1
;
p_sdp
->
i_media_type
=
atoi
(
psz_parse
);
}
else
{
msg_Dbg
(
p_obj
,
"incorrect m field, %s"
,
p_sdp
->
psz_media
);
p_sdp
->
i_media_type
=
33
;
psz_proto
=
strdup
(
psz_parse
);
}
char
*
subtype
=
strchr
(
sdp_proto
,
' '
);
if
(
sdp_proto
==
NULL
)
{
msg_Dbg
(
p_obj
,
"missing SDP media subtype: %s"
,
sdp_proto
);
p_sdp
->
i_media_type
=
0
;
}
if
(
psz_proto
&&
!
strncmp
(
psz_proto
,
"RTP/AVP"
,
7
)
)
else
{
free
(
psz_proto
)
;
p
sz_proto
=
strdup
(
"rtp"
);
*
subtype
++
=
'\0'
;
p
_sdp
->
i_media_type
=
atoi
(
subtype
);
}
if
(
psz_proto
&&
!
strncasecmp
(
psz_proto
,
"UDP"
,
3
)
)
if
(
p_sdp
->
i_media_type
==
0
)
p_sdp
->
i_media_type
=
33
;
static
const
char
proto_match
[]
=
"udp
\0
"
"udp
\0
"
"RTP/AVP
\0
"
"rtp
\0
"
"UDPLite/RTP/AVP
\0
"
"udplite
\0
"
"DCCP/RTP/AVP
\0
"
"dccp
\0
"
"TCP/RTP/AVP
\0
"
"tcp
\0
"
"
\0
"
;
const
char
*
vlc_proto
=
NULL
;
for
(
const
char
*
proto
=
proto_match
;
*
proto
;)
{
free
(
psz_proto
);
psz_proto
=
strdup
(
"udp"
);
if
(
strcasecmp
(
proto
,
sdp_proto
))
{
vlc_proto
=
proto
+
strlen
(
proto
)
+
1
;
break
;
}
proto
+=
strlen
(
proto
)
+
1
;
proto
+=
strlen
(
proto
)
+
1
;
}
if
(
i_port
==
0
)
free
(
sdp_proto
);
if
(
vlc_proto
==
NULL
)
{
i_port
=
1234
;
msg_Dbg
(
p_obj
,
"unknown SDP media protocol: %s"
,
p_sdp
->
mediav
[
0
].
fmt
);
return
VLC_EGENERIC
;
}
/* handle SSM case */
psz_parse
=
GetAttribute
(
p_sdp
,
"source-filter"
);
const
char
*
sfilter
=
GetAttribute
(
p_sdp
->
mediav
[
0
].
pp_attributes
,
p_sdp
->
mediav
[
0
].
i_attributes
,
"source-filter"
);
if
(
sfilter
==
NULL
)
sfilter
=
GetAttribute
(
p_sdp
->
pp_attributes
,
p_sdp
->
i_attributes
,
"source-filter"
);
char
psz_source
[
258
]
=
""
;
if
(
psz_parse
!=
NULL
)
if
(
sfilter
!=
NULL
)
{
char
psz_source_ip
[
256
];
if
(
sscanf
(
psz_parse
,
" incl IN IP%*c %*s %255s "
,
psz_source_ip
)
==
1
)
if
(
sscanf
(
sfilter
,
" incl IN IP%*c %*s %255s "
,
psz_source_ip
)
==
1
)
{
if
(
strchr
(
psz_source_ip
,
':'
)
!=
NULL
)
sprintf
(
psz_source
,
"[%s]"
,
psz_source_ip
);
...
...
@@ -993,13 +929,62 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
}
}
asprintf
(
&
p_sdp
->
psz_uri
,
"%s://%s@%s:%i"
,
psz
_proto
,
psz_source
,
psz_uri
,
i_
port
);
asprintf
(
&
p_sdp
->
psz_uri
,
"%s://%s@%s:%i"
,
vlc
_proto
,
psz_source
,
host
,
port
);
FREENULL
(
psz_proto
);
return
VLC_SUCCESS
;
}
static
int
ParseSDPConnection
(
const
char
*
str
,
struct
sockaddr_storage
*
addr
,
socklen_t
*
addrlen
,
unsigned
*
number
)
{
char
host
[
60
];
unsigned
fam
,
n1
,
n2
;
int
res
=
sscanf
(
str
,
"IN IP%u %59[^/]/%u/%u"
,
&
fam
,
host
,
&
n1
,
&
n2
);
if
(
res
<
2
)
return
-
1
;
switch
(
fam
)
{
#ifdef AF_INET6
case
6
:
addr
->
ss_family
=
AF_INET6
;
# ifdef HAVE_SA_LEN
addr
->
ss_len
=
# endif
*
addrlen
=
sizeof
(
struct
sockaddr_in6
);
if
(
inet_pton
(
AF_INET6
,
host
,
&
((
struct
sockaddr_in6
*
)
addr
)
->
sin6_addr
)
<=
0
)
return
-
1
;
*
number
=
(
res
>=
3
)
?
n1
:
1
;
break
;
#endif
case
4
:
addr
->
ss_family
=
AF_INET
;
# ifdef HAVE_SA_LEN
addr
->
ss_len
=
# endif
*
addrlen
=
sizeof
(
struct
sockaddr_in
);
if
(
inet_pton
(
AF_INET
,
host
,
&
((
struct
sockaddr_in
*
)
addr
)
->
sin_addr
)
<=
0
)
return
-
1
;
*
number
=
(
res
>=
4
)
?
n2
:
1
;
break
;
default:
return
-
1
;
}
return
0
;
}
/***********************************************************************
* ParseSDP : SDP parsing
* *********************************************************************
...
...
@@ -1007,187 +992,285 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
***********************************************************************/
static
sdp_t
*
ParseSDP
(
vlc_object_t
*
p_obj
,
const
char
*
psz_sdp
)
{
sdp_t
*
p_sdp
;
vlc_bool_t
b_invalid
=
VLC_FALSE
;
vlc_bool_t
b_end
=
VLC_FALSE
;
if
(
psz_sdp
==
NULL
)
{
return
NULL
;
}
if
(
psz_sdp
[
0
]
!=
'v'
||
psz_sdp
[
1
]
!=
'='
)
{
msg_Warn
(
p_obj
,
"bad packet"
);
return
NULL
;
}
p_
sdp
=
(
sdp_t
*
)
m
alloc
(
sizeof
(
sdp_t
)
);
if
(
p_sdp
==
NULL
)
sdp
_t
*
p_sdp
=
c
alloc
(
1
,
sizeof
(
*
p_sdp
)
);
if
(
p_sdp
==
NULL
)
return
NULL
;
/* init to 0 */
memset
(
p_sdp
,
0
,
sizeof
(
sdp_t
)
);
char
expect
=
'V'
;
struct
sockaddr_storage
glob_addr
;
memset
(
&
glob_addr
,
0
,
sizeof
(
glob_addr
));
socklen_t
glob_len
=
0
;
unsigned
glob_count
=
1
;
p_sdp
->
psz_sdp
=
strdup
(
psz_sdp
);
if
(
p_sdp
->
psz_sdp
==
NULL
)
/* TODO: use iconv and charset attribute instead of EnsureUTF8 */
while
(
*
psz_sdp
)
{
free
(
p_sdp
);
return
NULL
;
}
while
(
*
psz_sdp
!=
'\0'
&&
b_end
==
VLC_FALSE
)
{
char
*
psz_eol
=
NULL
;
char
*
psz_eof
=
NULL
;
char
*
psz_parse
=
NULL
;
char
*
psz_sess_id
=
NULL
;
while
(
*
psz_sdp
==
'\r'
||
*
psz_sdp
==
'\n'
||
*
psz_sdp
==
' '
||
*
psz_sdp
==
'\t'
)
/* Extract one line */
char
*
eol
=
strchr
(
psz_sdp
,
'\n'
);
size_t
linelen
=
eol
?
(
size_t
)(
eol
-
psz_sdp
)
:
strlen
(
psz_sdp
);
char
line
[
linelen
+
1
];
memcpy
(
line
,
psz_sdp
,
linelen
);
line
[
linelen
]
=
'\0'
;
psz_sdp
+=
linelen
+
1
;
/* Remove carriage return if present */
eol
=
strchr
(
line
,
'\r'
);
if
(
eol
!=
NULL
)
{
psz_sdp
++
;
linelen
=
eol
-
line
;
line
[
linelen
]
=
'\0'
;
}
if
(
(
psz_eol
=
strchr
(
psz_sdp
,
'\n'
)
)
==
NULL
)
{
psz_eol
=
(
char
*
)
psz_sdp
+
strlen
(
psz_sdp
);
b_end
=
VLC_TRUE
;
}
if
(
psz_eol
>
psz_sdp
&&
*
(
psz_eol
-
1
)
==
'\r'
)
/* Validate line */
char
cat
=
line
[
0
],
*
data
=
line
+
2
;
if
(
!
cat
||
(
strchr
(
"vosiuepcbtrzkam"
,
cat
)
==
NULL
))
{
psz_eol
--
;
/* MUST ignore SDP with unknown line type */
msg_Dbg
(
p_obj
,
"unknown SDP line type: 0x%02x"
,
(
int
)
cat
);
goto
error
;
}
if
(
psz_eol
<=
psz_sdp
)
if
(
line
[
1
]
!=
'='
)
{
break
;
msg_Dbg
(
p_obj
,
"invalid SDP line: %s"
,
line
);
goto
error
;
}
*
psz_eol
++
=
'\0'
;
/* no space allowed between fields */
if
(
psz_sdp
[
1
]
!=
'='
)
{
msg_Warn
(
p_obj
,
"invalid packet"
)
;
FreeSDP
(
p_sdp
);
p_sdp
=
NULL
;
return
NULL
;
}
assert
(
linelen
>=
2
);
/* Now parse each line */
switch
(
psz_sdp
[
0
]
)
/* SDP parsing state machine
* We INTERNALLY use uppercase for session, lowercase for media
*/
switch
(
expect
)
{
case
(
'v'
):
break
;
case
(
's'
):
p_sdp
->
psz_sessionname
=
strdup
(
&
psz_sdp
[
2
]
);
/* Session description */
case
'V'
:
expect
=
'O'
;
if
(
cat
!=
'v'
)
{
msg_Dbg
(
p_obj
,
"missing SDP version"
);
goto
error
;
}
if
(
strcmp
(
data
,
"0"
))
{
msg_Dbg
(
p_obj
,
"unknown SDP version: %s"
,
data
);
goto
error
;
}
break
;
case
(
'o'
):
case
'O'
:
{
int
i_field
=
0
;
/* o field is <username> <session id> <version>
* <network type> <address type> <address> */
#define GET_FIELD( store ) \
psz_eof = strchr( psz_parse, ' ' ); \
if( psz_eof ) \
{ \
*psz_eof=0; store = strdup( psz_parse ); \
} \
else \
{ \