From 5f9984e8666595f5de228ebe238ed06a5265870a Mon Sep 17 00:00:00 2001 From: Peter Kjellerstedt Date: Sat, 12 May 2007 16:26:06 +0000 Subject: [PATCH] gst/rtsp/rtsptransport.*: Add validation to rtsp_transport_parse(). Original commit message from CVS: Patch by: Peter Kjellerstedt * gst/rtsp/rtsptransport.c: (rtsp_transport_init), (parse_mode), (parse_range), (range_as_text), (rtsp_transport_mode_as_text), (rtsp_transport_profile_as_text), (rtsp_transport_ltrans_as_text), (rtsp_transport_parse), (rtsp_transport_as_text): * gst/rtsp/rtsptransport.h: Add validation to rtsp_transport_parse(). Add rtsp_transport_as_text() to generate an RTSP header from an RTSPTransport. Change ssrc to guint (was a string) since that is what it is, even though it is sent as a hex string. Correctly identify PLAY|RECORD mode parameters (the syntax in the RFC is incorrect, which can be seen when looking at the examples in the RFC). Fixes #437670. --- ChangeLog | 18 ++ gst/rtsp/rtsptransport.c | 397 +++++++++++++++++++++++++++++++++++---- gst/rtsp/rtsptransport.h | 9 +- 3 files changed, 384 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index 173bf90931..ec4f6b588c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2007-05-12 Wim Taymans + + Patch by: Peter Kjellerstedt + + * gst/rtsp/rtsptransport.c: (rtsp_transport_init), (parse_mode), + (parse_range), (range_as_text), (rtsp_transport_mode_as_text), + (rtsp_transport_profile_as_text), (rtsp_transport_ltrans_as_text), + (rtsp_transport_parse), (rtsp_transport_as_text): + * gst/rtsp/rtsptransport.h: + Add validation to rtsp_transport_parse(). + Add rtsp_transport_as_text() to generate an RTSP header from an + RTSPTransport. + Change ssrc to guint (was a string) since that is what it is, even + though it is sent as a hex string. + Correctly identify PLAY|RECORD mode parameters (the syntax in the RFC is + incorrect, which can be seen when looking at the examples in the RFC). + Fixes #437670. + 2007-05-11 Zaheer Abbas Merali <> Patch by: Eric Anholt diff --git a/gst/rtsp/rtsptransport.c b/gst/rtsp/rtsptransport.c index 6dcb76be05..832604140c 100644 --- a/gst/rtsp/rtsptransport.c +++ b/gst/rtsp/rtsptransport.c @@ -47,6 +47,22 @@ #define MAX_MANAGERS 2 +typedef enum +{ + RTSP_TRANSPORT_DELIVERY = 1 << 0, /* multicast | unicast */ + RTSP_TRANSPORT_DESTINATION = 1 << 1, + RTSP_TRANSPORT_SOURCE = 1 << 2, + RTSP_TRANSPORT_INTERLEAVED = 1 << 3, + RTSP_TRANSPORT_APPEND = 1 << 4, + RTSP_TRANSPORT_TTL = 1 << 5, + RTSP_TRANSPORT_LAYERS = 1 << 6, + RTSP_TRANSPORT_PORT = 1 << 7, + RTSP_TRANSPORT_CLIENT_PORT = 1 << 8, + RTSP_TRANSPORT_SERVER_PORT = 1 << 9, + RTSP_TRANSPORT_SSRC = 1 << 10, + RTSP_TRANSPORT_MODE = 1 << 11, +} RTSPTransportParameter; + typedef struct { const gchar *name; @@ -84,9 +100,16 @@ static const RTSPLTransMap ltrans[] = { {"udp", RTSP_LOWER_TRANS_UDP}, {"mcast", RTSP_LOWER_TRANS_UDP_MCAST}, {"tcp", RTSP_LOWER_TRANS_TCP}, - {NULL, RTSP_LOWER_TRANS_UDP_MCAST} /* UDP is default */ + {NULL, RTSP_LOWER_TRANS_UNKNOWN} }; +#define RTSP_TRANSPORT_PARAMETER_IS_UNIQUE(param) \ +G_STMT_START { \ + if ((transport_params & (param)) != 0) \ + goto invalid_transport; \ + transport_params |= (param); \ +} G_STMT_END + RTSPResult rtsp_transport_new (RTSPTransport ** transport) { @@ -108,15 +131,22 @@ rtsp_transport_init (RTSPTransport * transport) g_free (transport->destination); g_free (transport->source); - g_free (transport->ssrc); memset (transport, 0, sizeof (RTSPTransport)); transport->trans = RTSP_TRANS_RTP; transport->profile = RTSP_PROFILE_AVP; - transport->lower_transport = RTSP_LOWER_TRANS_UDP; + transport->lower_transport = RTSP_LOWER_TRANS_UDP_MCAST; transport->mode_play = TRUE; transport->mode_record = FALSE; + transport->interleaved.min = -1; + transport->interleaved.max = -1; + transport->port.min = -1; + transport->port.max = -1; + transport->client_port.min = -1; + transport->client_port.max = -1; + transport->server_port.min = -1; + transport->server_port.max = -1; return RTSP_OK; } @@ -157,31 +187,110 @@ rtsp_transport_get_manager (RTSPTransMode trans, const gchar ** manager, } static void -parse_mode (RTSPTransport * transport, gchar * str) +parse_mode (RTSPTransport * transport, const gchar * str) { - transport->mode_play = (strstr (str, "\"play\"") != NULL); - transport->mode_record = (strstr (str, "\"record\"") != NULL); + transport->mode_play = (strstr (str, "play") != NULL); + transport->mode_record = (strstr (str, "record") != NULL); } static void -parse_range (RTSPTransport * transport, gchar * str, RTSPRange * range) +parse_range (const gchar * str, RTSPRange * range) { gchar *minus; + gchar *tmp; + + /* even though strtol() allows white space, plus and minus in front of + * the number, we do not allow it + */ + if (g_ascii_isspace (*str) || *str == '+' || *str == '-') + goto invalid_range; minus = strstr (str, "-"); if (minus) { - range->min = atoi (str); - range->max = atoi (minus + 1); + if (g_ascii_isspace (minus[1]) || minus[1] == '+' || minus[1] == '-') + goto invalid_range; + + range->min = strtol (str, &tmp, 10); + if (str == tmp || tmp != minus) + goto invalid_range; + + range->max = strtol (minus + 1, &tmp, 10); + if (*tmp && *tmp != ';') + goto invalid_range; } else { - range->min = atoi (str); + range->min = strtol (str, &tmp, 10); + if (str == tmp || (*tmp && *tmp != ';')) + goto invalid_range; + range->max = -1; } + + return; + +invalid_range: + { + range->min = -1; + range->max = -1; + return; + } +} + +static gchar * +range_as_text (const RTSPRange * range) +{ + if (range->min < 0) + return NULL; + else if (range->max < 0) + return g_strdup_printf ("%d", range->min); + else + return g_strdup_printf ("%d-%d", range->min, range->max); +} + +static const gchar * +rtsp_transport_mode_as_text (const RTSPTransport * transport) +{ + gint i; + + for (i = 0; transports[i].name; i++) + if (transports[i].mode == transport->trans) + return transports[i].name; + + return NULL; +} + +static const gchar * +rtsp_transport_profile_as_text (const RTSPTransport * transport) +{ + gint i; + + for (i = 0; profiles[i].name; i++) + if (profiles[i].profile == transport->profile) + return profiles[i].name; + + return NULL; +} + +static const gchar * +rtsp_transport_ltrans_as_text (const RTSPTransport * transport) +{ + gint i; + + /* need to special case RTSP_LOWER_TRANS_UDP_MCAST */ + if (transport->lower_transport == RTSP_LOWER_TRANS_UDP_MCAST) + return "udp"; + + for (i = 0; ltrans[i].name; i++) + if (ltrans[i].ltrans == transport->lower_transport) + return ltrans[i].name; + + return NULL; } RTSPResult rtsp_transport_parse (const gchar * str, RTSPTransport * transport) { - gchar **split, *down; + gchar **split, *down, **transp = NULL; + guint transport_params = 0; gint i; g_return_val_if_fail (transport != NULL, RTSP_EINVAL); @@ -193,52 +302,112 @@ rtsp_transport_parse (const gchar * str, RTSPTransport * transport) down = g_ascii_strdown (str, -1); split = g_strsplit (down, ";", 0); + g_free (down); /* First field contains the transport/profile/lower_transport */ - i = 0; - if (split[0]) { - for (i = 0; transports[i].name; i++) - if (strstr (split[0], transports[i].name)) - break; - transport->trans = transports[i].mode; - for (i = 0; profiles[i].name; i++) - if (strstr (split[0], profiles[i].name)) - break; - transport->profile = profiles[i].profile; + if (split[0] == NULL) + goto invalid_transport; + + transp = g_strsplit (split[0], "/", 0); + + if (transp[0] == NULL || transp[1] == NULL) + goto invalid_transport; + + for (i = 0; transports[i].name; i++) + if (strcmp (transp[0], transports[i].name) == 0) + break; + transport->trans = transports[i].mode; + + for (i = 0; profiles[i].name; i++) + if (strcmp (transp[1], profiles[i].name) == 0) + break; + transport->profile = profiles[i].profile; + + if (transp[2] != NULL) { for (i = 0; ltrans[i].name; i++) - if (strstr (split[0], ltrans[i].name)) + if (strcmp (transp[2], ltrans[i].name) == 0) break; transport->lower_transport = ltrans[i].ltrans; - i = 1; - } - while (split[i]) { - if (g_str_has_prefix (split[i], "multicast")) { + } else { + /* specifying the lower transport is optional */ + if (transport->trans == RTSP_TRANS_RTP && + transport->profile == RTSP_PROFILE_AVP) transport->lower_transport = RTSP_LOWER_TRANS_UDP_MCAST; - } else if (g_str_has_prefix (split[i], "unicast")) { + else + transport->lower_transport = RTSP_LOWER_TRANS_UNKNOWN; + } + + g_strfreev (transp); + transp = NULL; + + if (transport->trans == RTSP_TRANS_UNKNOWN || + transport->profile == RTSP_PROFILE_UNKNOWN || + transport->lower_transport == RTSP_LOWER_TRANS_UNKNOWN) + goto unsupported_transport; + + i = 1; + while (split[i]) { + if (strcmp (split[i], "multicast") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DELIVERY); + if (transport->lower_transport == RTSP_LOWER_TRANS_TCP) + goto invalid_transport; + transport->lower_transport = RTSP_LOWER_TRANS_UDP_MCAST; + } else if (strcmp (split[i], "unicast") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DELIVERY); if (transport->lower_transport == RTSP_LOWER_TRANS_UDP_MCAST) transport->lower_transport = RTSP_LOWER_TRANS_UDP; } else if (g_str_has_prefix (split[i], "destination=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DESTINATION); transport->destination = g_strdup (split[i] + 12); } else if (g_str_has_prefix (split[i], "source=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SOURCE); transport->source = g_strdup (split[i] + 7); } else if (g_str_has_prefix (split[i], "layers=")) { - transport->layers = atoi (split[i] + 7); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_LAYERS); + transport->layers = strtoul (split[i] + 7, NULL, 10); } else if (g_str_has_prefix (split[i], "mode=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_MODE); parse_mode (transport, split[i] + 5); - } else if (g_str_has_prefix (split[i], "append")) { + if (!transport->mode_play && !transport->mode_record) + goto invalid_transport; + } else if (strcmp (split[i], "append") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_APPEND); transport->append = TRUE; } else if (g_str_has_prefix (split[i], "interleaved=")) { - parse_range (transport, split[i] + 12, &transport->interleaved); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_INTERLEAVED); + parse_range (split[i] + 12, &transport->interleaved); + if (transport->interleaved.min < 0 || + transport->interleaved.min >= 256 || + transport->interleaved.max >= 256) + goto invalid_transport; } else if (g_str_has_prefix (split[i], "ttl=")) { - transport->ttl = atoi (split[i] + 4); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_TTL); + transport->ttl = strtoul (split[i] + 4, NULL, 10); + if (transport->ttl >= 256) + goto invalid_transport; } else if (g_str_has_prefix (split[i], "port=")) { - parse_range (transport, split[i] + 5, &transport->port); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_PORT); + parse_range (split[i] + 5, &transport->port); + if (transport->port.min < 0 || + transport->port.min >= 65536 || transport->port.max >= 65536) + goto invalid_transport; } else if (g_str_has_prefix (split[i], "client_port=")) { - parse_range (transport, split[i] + 12, &transport->client_port); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_CLIENT_PORT); + parse_range (split[i] + 12, &transport->client_port); + if (transport->client_port.min < 0 || + transport->client_port.min >= 65536 || + transport->client_port.max >= 65536) + goto invalid_transport; } else if (g_str_has_prefix (split[i], "server_port=")) { - parse_range (transport, split[i] + 12, &transport->server_port); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SERVER_PORT); + parse_range (split[i] + 12, &transport->server_port); + if (transport->server_port.min < 0 || + transport->server_port.min >= 65536 || + transport->server_port.max >= 65536) + goto invalid_transport; } else if (g_str_has_prefix (split[i], "ssrc=")) { - transport->ssrc = g_strdup (split[i] + 5); + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SSRC); + transport->ssrc = strtoul (split[i] + 5, NULL, 16); } else { /* unknown field... */ g_warning ("unknown transport field \"%s\"", split[i]); @@ -246,9 +415,165 @@ rtsp_transport_parse (const gchar * str, RTSPTransport * transport) i++; } g_strfreev (split); - g_free (down); return RTSP_OK; + +unsupported_transport: + { + g_strfreev (split); + return RTSP_ERROR; + } +invalid_transport: + { + g_strfreev (transp); + g_strfreev (split); + return RTSP_EINVAL; + } +} + +gchar * +rtsp_transport_as_text (RTSPTransport * transport) +{ + GPtrArray *strs; + gchar *res; + const gchar *tmp; + + g_return_val_if_fail (transport != NULL, NULL); + + strs = g_ptr_array_new (); + + /* add the transport specifier */ + if ((tmp = rtsp_transport_mode_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + + g_ptr_array_add (strs, g_strdup ("/")); + + if ((tmp = rtsp_transport_profile_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + + if (transport->trans != RTSP_TRANS_RTP || + transport->profile != RTSP_PROFILE_AVP || + transport->lower_transport == RTSP_LOWER_TRANS_TCP) { + g_ptr_array_add (strs, g_strdup ("/")); + + if ((tmp = rtsp_transport_ltrans_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + } + + /* + * the order of the following parameters is the same as the one specified in + * RFC 2326 to please some weird RTSP clients that require it + */ + + /* add the unicast/multicast parameter */ + if (transport->lower_transport == RTSP_LOWER_TRANS_UDP_MCAST) + g_ptr_array_add (strs, g_strdup (";multicast")); + else + g_ptr_array_add (strs, g_strdup (";unicast")); + + /* add the destination parameter */ + if (transport->destination != NULL) { + g_ptr_array_add (strs, g_strdup (";destination=")); + g_ptr_array_add (strs, g_strdup (transport->destination)); + } + + /* add the source parameter */ + if (transport->source != NULL) { + g_ptr_array_add (strs, g_strdup (";source=")); + g_ptr_array_add (strs, g_strdup (transport->source)); + } + + /* add the interleaved parameter */ + if (transport->lower_transport == RTSP_LOWER_TRANS_TCP && + transport->interleaved.min >= 0) { + if (transport->interleaved.min < 256 && transport->interleaved.max < 256) { + g_ptr_array_add (strs, g_strdup (";interleaved=")); + g_ptr_array_add (strs, range_as_text (&transport->interleaved)); + } else + goto invalid_transport; + } + + /* add the append parameter */ + if (transport->mode_record && transport->append) + g_ptr_array_add (strs, g_strdup (";append")); + + /* add the ttl parameter */ + if (transport->lower_transport == RTSP_LOWER_TRANS_UDP_MCAST && + transport->ttl != 0) { + if (transport->ttl < 256) { + g_ptr_array_add (strs, g_strdup (";ttl=")); + g_ptr_array_add (strs, g_strdup_printf ("%u", transport->ttl)); + } else + goto invalid_transport; + } + + /* add the layers parameter */ + if (transport->layers != 0) { + g_ptr_array_add (strs, g_strdup (";layers=")); + g_ptr_array_add (strs, g_strdup_printf ("%u", transport->layers)); + } + + /* add the port parameter */ + if (transport->trans == RTSP_TRANS_RTP && transport->port.min >= 0) { + if (transport->port.min < 65536 && transport->port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";port=")); + g_ptr_array_add (strs, range_as_text (&transport->port)); + } else + goto invalid_transport; + } + + /* add the client_port parameter */ + if (transport->trans == RTSP_TRANS_RTP && transport->client_port.min >= 0) { + if (transport->client_port.min < 65536 && + transport->client_port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";client_port=")); + g_ptr_array_add (strs, range_as_text (&transport->client_port)); + } else + goto invalid_transport; + } + + /* add the server_port parameter */ + if (transport->trans == RTSP_TRANS_RTP && transport->server_port.min >= 0) { + if (transport->server_port.min < 65536 && + transport->server_port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";server_port=")); + g_ptr_array_add (strs, range_as_text (&transport->server_port)); + } else + goto invalid_transport; + } + + /* add the ssrc parameter */ + if (transport->lower_transport != RTSP_LOWER_TRANS_UDP_MCAST && + transport->ssrc != 0) { + g_ptr_array_add (strs, g_strdup (";ssrc=")); + g_ptr_array_add (strs, g_strdup_printf ("%08X", transport->ssrc)); + } + + /* add the mode parameter */ + if (transport->mode_play && transport->mode_record) + g_ptr_array_add (strs, g_strdup (";mode=\"PLAY,RECORD\"")); + else if (transport->mode_record) + g_ptr_array_add (strs, g_strdup (";mode=\"RECORD\"")); + else if (transport->mode_play) + g_ptr_array_add (strs, g_strdup (";mode=\"PLAY\"")); + + /* add a terminating NULL */ + g_ptr_array_add (strs, NULL); + + res = g_strjoinv (NULL, (gchar **) strs->pdata); + g_strfreev ((gchar **) g_ptr_array_free (strs, FALSE)); + + return res; + +invalid_transport: + { + g_ptr_array_add (strs, NULL); + g_strfreev ((gchar **) g_ptr_array_free (strs, FALSE)); + return NULL; + } } RTSPResult diff --git a/gst/rtsp/rtsptransport.h b/gst/rtsp/rtsptransport.h index 0581839108..70497ab880 100644 --- a/gst/rtsp/rtsptransport.h +++ b/gst/rtsp/rtsptransport.h @@ -117,28 +117,29 @@ typedef struct _RTSPTransport { gchar *destination; gchar *source; - gint layers; + guint layers; gboolean mode_play; gboolean mode_record; gboolean append; RTSPRange interleaved; /* multicast specific */ - gint ttl; + guint ttl; /* UDP specific */ RTSPRange port; RTSPRange client_port; RTSPRange server_port; /* RTP specific */ - gchar *ssrc; - + guint ssrc; + } RTSPTransport; RTSPResult rtsp_transport_new (RTSPTransport **transport); RTSPResult rtsp_transport_init (RTSPTransport *transport); RTSPResult rtsp_transport_parse (const gchar *str, RTSPTransport *transport); +gchar *rtsp_transport_as_text (RTSPTransport *transport); RTSPResult rtsp_transport_get_mime (RTSPTransMode trans, const gchar **mime); RTSPResult rtsp_transport_get_manager (RTSPTransMode trans, const gchar **manager, guint option);