diff --git a/gst-libs/gst/rtsp/gstrtspconnection.c b/gst-libs/gst/rtsp/gstrtspconnection.c index dba9b7bbc1..d6773b17fc 100644 --- a/gst-libs/gst/rtsp/gstrtspconnection.c +++ b/gst-libs/gst/rtsp/gstrtspconnection.c @@ -198,6 +198,8 @@ struct _GstRTSPConnection gchar *passwd; GHashTable *auth_params; + guint content_length_limit; + /* TLS */ GTlsDatabase *tls_database; GTlsInteraction *tls_interaction; @@ -240,7 +242,7 @@ typedef struct guint line; guint8 *body_data; - glong body_len; + guint body_len; } GstRTSPBuilder; /* function prototypes */ @@ -370,6 +372,8 @@ gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn) newconn->auth_params = NULL; newconn->version = 0; + newconn->content_length_limit = G_MAXUINT; + *conn = newconn; return GST_RTSP_OK; @@ -2417,6 +2421,7 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message, /* we have a regular response */ if (builder->buffer[0] == '\0') { gchar *hdrval; + gint64 content_length_parsed = 0; /* empty line, end of message header */ /* see if there is a Content-Length header, but ignore it if this @@ -2428,13 +2433,23 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message, gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) { /* there is, prepare to read the body */ - builder->body_len = atol (hdrval); + errno = 0; + content_length_parsed = g_ascii_strtoll (hdrval, NULL, 10); + if (errno != 0 || content_length_parsed < 0) { + res = GST_RTSP_EPARSE; + goto invalid_body_len; + } else if (content_length_parsed > conn->content_length_limit) { + res = GST_RTSP_ENOMEM; + goto invalid_body_len; + } + builder->body_len = content_length_parsed; builder->body_data = g_try_malloc (builder->body_len + 1); /* we can't do much here, we need the length to know how many bytes - * we need to read next and when allocation fails, something is - * probably wrong with the length. */ - if (builder->body_data == NULL) + * we need to read next and when allocation fails, we can't read the payload. */ + if (builder->body_data == NULL) { + res = GST_RTSP_ENOMEM; goto invalid_body_len; + } builder->body_data[builder->body_len] = '\0'; builder->offset = 0; @@ -2549,7 +2564,7 @@ invalid_body_len: { conn->may_cancel = TRUE; GST_DEBUG ("could not allocate body"); - return GST_RTSP_ERROR; + return res; } invalid_format: { @@ -3237,6 +3252,25 @@ gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp) return res; } +/** + * gst_rtsp_connection_set_content_length_limit: + * @conn: a #GstRTSPConnection + * @limit: Content-Length limit + * + * Configure @conn to use the specified Content-Length limit. + * Both requests and responses are validated. If content-length is + * exceeded, ENOMEM error will be returned. + * + * Since: 1.18 + */ +void +gst_rtsp_connection_set_content_length_limit (GstRTSPConnection * conn, + guint limit) +{ + g_return_if_fail (conn != NULL); + + conn->content_length_limit = limit; +} /** * gst_rtsp_connection_get_url: diff --git a/gst-libs/gst/rtsp/gstrtspconnection.h b/gst-libs/gst/rtsp/gstrtspconnection.h index b27f813105..70498c5d68 100644 --- a/gst-libs/gst/rtsp/gstrtspconnection.h +++ b/gst-libs/gst/rtsp/gstrtspconnection.h @@ -189,6 +189,11 @@ GST_RTSP_API GstRTSPResult gst_rtsp_connection_set_qos_dscp (GstRTSPConnection *conn, guint qos_dscp); +/* Content-Length limit */ +GST_RTSP_API +void gst_rtsp_connection_set_content_length_limit (GstRTSPConnection *conn, + guint limit); + /* accessors */ GST_RTSP_API diff --git a/tests/check/libs/rtspconnection.c b/tests/check/libs/rtspconnection.c index 56f83e5b96..ae3e280add 100644 --- a/tests/check/libs/rtspconnection.c +++ b/tests/check/libs/rtspconnection.c @@ -859,6 +859,75 @@ GST_START_TEST (test_rtspconnection_ip) GST_END_TEST; +GST_START_TEST (test_rtspconnection_send_receive_content_length) +{ + GSocketConnection *input_conn = NULL; + GSocketConnection *output_conn = NULL; + GSocket *input_sock; + GSocket *output_sock; + GstRTSPConnection *rtsp_output_conn; + GstRTSPConnection *rtsp_input_conn; + GstRTSPMessage *msg; + + create_connection (&input_conn, &output_conn); + input_sock = g_socket_connection_get_socket (input_conn); + fail_unless (input_sock != NULL); + output_sock = g_socket_connection_get_socket (output_conn); + fail_unless (output_sock != NULL); + + fail_unless (gst_rtsp_connection_create_from_socket (input_sock, "127.0.0.1", + 4444, NULL, &rtsp_input_conn) == GST_RTSP_OK); + fail_unless (rtsp_input_conn != NULL); + + fail_unless (gst_rtsp_connection_create_from_socket (output_sock, "127.0.0.1", + 4444, NULL, &rtsp_output_conn) == GST_RTSP_OK); + fail_unless (rtsp_output_conn != NULL); + + /* send request message with to big payload */ + fail_unless (gst_rtsp_message_new_request (&msg, GST_RTSP_SETUP, + "rtsp://example.com/") == GST_RTSP_OK); + fail_unless (gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, + "2000") == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_send (rtsp_output_conn, msg, + NULL) == GST_RTSP_OK); + fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK); + msg = NULL; + + /* receive request message, expect ENOMEM */ + gst_rtsp_connection_set_content_length_limit (rtsp_input_conn, 1000); + fail_unless (gst_rtsp_message_new (&msg) == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_receive (rtsp_input_conn, msg, NULL) == + GST_RTSP_ENOMEM); + fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK); + msg = NULL; + + /* send request message with negative payload */ + fail_unless (gst_rtsp_message_new_request (&msg, GST_RTSP_SETUP, + "rtsp://example.com/") == GST_RTSP_OK); + fail_unless (gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, + "-2000") == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_send (rtsp_output_conn, msg, + NULL) == GST_RTSP_OK); + fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK); + msg = NULL; + + /* receive request message, expect EPARSE */ + fail_unless (gst_rtsp_message_new (&msg) == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_receive (rtsp_input_conn, msg, NULL) == + GST_RTSP_EPARSE); + fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK); + msg = NULL; + + fail_unless (gst_rtsp_connection_close (rtsp_input_conn) == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_free (rtsp_input_conn) == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_close (rtsp_output_conn) == GST_RTSP_OK); + fail_unless (gst_rtsp_connection_free (rtsp_output_conn) == GST_RTSP_OK); + + g_object_unref (input_conn); + g_object_unref (output_conn); +} + +GST_END_TEST; static Suite * rtspconnection_suite (void) @@ -875,6 +944,7 @@ rtspconnection_suite (void) tcase_add_test (tc_chain, test_rtspconnection_poll); tcase_add_test (tc_chain, test_rtspconnection_backlog); tcase_add_test (tc_chain, test_rtspconnection_ip); + tcase_add_test (tc_chain, test_rtspconnection_send_receive_content_length); return s; }