From f12fb76f70cbc7ecb088fa9ca00b33b3079c9d8e Mon Sep 17 00:00:00 2001 From: Peter Kjellerstedt Date: Fri, 1 Jun 2007 13:07:11 +0000 Subject: [PATCH] gst/rtsp/: Improves version checking, allowing an RTSP server to reply with "505 Original commit message from CVS: Patch by: Peter Kjellerstedt * gst/rtsp/rtspconnection.c: (rtsp_connection_create), (rtsp_connection_connect), (add_date_header), (rtsp_connection_send), (parse_response_status), (parse_request_line), (parse_line), (rtsp_connection_receive): * gst/rtsp/rtspdefs.c: (rtsp_version_as_text): * gst/rtsp/rtspdefs.h: * gst/rtsp/rtspmessage.c: (key_value_foreach), (rtsp_message_init_request), (rtsp_message_init_response), (rtsp_message_remove_header), (rtsp_message_append_headers), (rtsp_message_dump): * gst/rtsp/rtspmessage.h: Improves version checking, allowing an RTSP server to reply with "505 RTSP Version not supported. Adds a Date header to all messages. Replies with RTSP_EPARSE rather than RTSP_EINVALID in cases where we want to be able to send a response even if something in the request was invalid. EINVAL is only used when passing wrong arguments to functions. Do not handle an invalid method in parse_request_line(). Defer this to the caller so it can respond with "405 Method Not Allowed". Improves parsing of the timeout parameter to the Session header, allowing whitespace after the semicolon. Avoids a compiler warning due to variables shadowing a function argument. --- ChangeLog | 27 ++++++++ gst/rtsp/rtspconnection.c | 141 ++++++++++++++++++++++---------------- gst/rtsp/rtspdefs.c | 12 ++++ gst/rtsp/rtspdefs.h | 6 ++ gst/rtsp/rtspmessage.c | 14 ++-- gst/rtsp/rtspmessage.h | 2 + 6 files changed, 140 insertions(+), 62 deletions(-) diff --git a/ChangeLog b/ChangeLog index 00ed607b93..b96ad785be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2007-06-01 Wim Taymans + + Patch by: Peter Kjellerstedt + + * gst/rtsp/rtspconnection.c: (rtsp_connection_create), + (rtsp_connection_connect), (add_date_header), + (rtsp_connection_send), (parse_response_status), + (parse_request_line), (parse_line), (rtsp_connection_receive): + * gst/rtsp/rtspdefs.c: (rtsp_version_as_text): + * gst/rtsp/rtspdefs.h: + * gst/rtsp/rtspmessage.c: (key_value_foreach), + (rtsp_message_init_request), (rtsp_message_init_response), + (rtsp_message_remove_header), (rtsp_message_append_headers), + (rtsp_message_dump): + * gst/rtsp/rtspmessage.h: + Improves version checking, allowing an RTSP server to reply with "505 + RTSP Version not supported. + Adds a Date header to all messages. + Replies with RTSP_EPARSE rather than RTSP_EINVALID in cases where we + want to be able to send a response even if something in the request was + invalid. EINVAL is only used when passing wrong arguments to functions. + Do not handle an invalid method in parse_request_line(). Defer this to + the caller so it can respond with "405 Method Not Allowed". + Improves parsing of the timeout parameter to the Session header, + allowing whitespace after the semicolon. + Avoids a compiler warning due to variables shadowing a function argument. + 2007-06-01 Wim Taymans Based on Patch by: Daniel Charles diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c index e20de2314c..50948f6cea 100644 --- a/gst/rtsp/rtspconnection.c +++ b/gst/rtsp/rtspconnection.c @@ -122,7 +122,7 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn) g_return_val_if_fail (conn != NULL, RTSP_EINVAL); - newconn = g_new (RTSPConnection, 1); + newconn = g_new0 (RTSPConnection, 1); #ifdef G_OS_WIN32 /* This should work on UNIX too. PF_UNIX sockets replaced with pipe */ @@ -140,8 +140,6 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn) newconn->url = url; newconn->fd = -1; - newconn->cseq = 0; - newconn->session_id[0] = 0; newconn->timer = g_timer_new (); newconn->auth_method = RTSP_AUTH_NONE; @@ -164,7 +162,7 @@ RTSPResult rtsp_connection_connect (RTSPConnection * conn, GTimeVal * timeout) { gint fd; - struct sockaddr_in sin; + struct sockaddr_in sa_in; struct hostent *hostinfo; char **addrs; gchar *ip; @@ -201,10 +199,10 @@ rtsp_connection_connect (RTSPConnection * conn, GTimeVal * timeout) /* get the port from the url */ rtsp_url_get_port (url, &port); - memset (&sin, 0, sizeof (sin)); - sin.sin_family = AF_INET; /* network socket */ - sin.sin_port = htons (port); /* on port */ - sin.sin_addr.s_addr = inet_addr (ip); /* on host ip */ + memset (&sa_in, 0, sizeof (sa_in)); + sa_in.sin_family = AF_INET; /* network socket */ + sa_in.sin_port = htons (port); /* on port */ + sa_in.sin_addr.s_addr = inet_addr (ip); /* on host ip */ fd = socket (AF_INET, SOCK_STREAM, 0); if (fd == -1) @@ -214,7 +212,7 @@ rtsp_connection_connect (RTSPConnection * conn, GTimeVal * timeout) fcntl (fd, F_SETFL, O_NONBLOCK); /* we are going to connect ASYNC now */ - ret = connect (fd, (struct sockaddr *) &sin, sizeof (sin)); + ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in)); if (ret == 0) goto done; if (errno != EINPROGRESS) @@ -295,6 +293,19 @@ add_auth_header (RTSPConnection * conn, RTSPMessage * message) } } +static void +add_date_header (RTSPMessage * message) +{ + GTimeVal tv; + gchar date_string[100]; + + g_get_current_time (&tv); + strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT", + gmtime (&tv.tv_sec)); + + rtsp_message_add_header (message, RTSP_HDR_DATE, date_string); +} + RTSPResult rtsp_connection_write (RTSPConnection * conn, const guint8 * data, guint size, GTimeVal * timeout) @@ -389,7 +400,7 @@ RTSPResult rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message, GTimeVal * timeout) { - GString *str = NULL; + GString *str; RTSPResult res; #ifdef G_OS_WIN32 @@ -455,6 +466,9 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message, /* append headers and body */ if (message->type != RTSP_MESSAGE_DATA) { + /* add date header */ + add_date_header (message); + /* append headers */ rtsp_message_append_headers (message, str); @@ -576,6 +590,7 @@ read_key (gchar * dest, gint size, gchar ** src) static RTSPResult parse_response_status (gchar * buffer, RTSPMessage * msg) { + RTSPResult res; gchar versionstr[20]; gchar codestr[4]; gint code; @@ -584,28 +599,34 @@ parse_response_status (gchar * buffer, RTSPMessage * msg) bptr = buffer; read_string (versionstr, sizeof (versionstr), &bptr); - if (strcmp (versionstr, "RTSP/1.0") != 0) - goto wrong_version; - read_string (codestr, sizeof (codestr), &bptr); code = atoi (codestr); while (g_ascii_isspace (*bptr)) bptr++; - rtsp_message_init_response (msg, code, bptr, NULL); + if (strcmp (versionstr, "RTSP/1.0") == 0) + RTSP_CHECK (rtsp_message_init_response (msg, code, bptr, NULL), + parse_error); + else if (strncmp (versionstr, "RTSP/", 5) == 0) { + RTSP_CHECK (rtsp_message_init_response (msg, code, bptr, NULL), + parse_error); + msg->type_data.response.version = RTSP_VERSION_INVALID; + } else + goto parse_error; return RTSP_OK; -wrong_version: +parse_error: { - return RTSP_EINVAL; + return RTSP_EPARSE; } } static RTSPResult parse_request_line (gchar * buffer, RTSPMessage * msg) { + RTSPResult res = RTSP_OK; gchar versionstr[20]; gchar methodstr[20]; gchar urlstr[4096]; @@ -616,27 +637,30 @@ parse_request_line (gchar * buffer, RTSPMessage * msg) read_string (methodstr, sizeof (methodstr), &bptr); method = rtsp_find_method (methodstr); - if (method == RTSP_INVALID) - goto wrong_method; read_string (urlstr, sizeof (urlstr), &bptr); + if (*urlstr == '\0') + res = RTSP_EPARSE; read_string (versionstr, sizeof (versionstr), &bptr); - if (strcmp (versionstr, "RTSP/1.0") != 0) - goto wrong_version; - rtsp_message_init_request (msg, method, urlstr); + if (*bptr != '\0') + res = RTSP_EPARSE; - return RTSP_OK; - -wrong_method: - { - return RTSP_EINVAL; - } -wrong_version: - { - return RTSP_EINVAL; + if (strcmp (versionstr, "RTSP/1.0") == 0) { + if (rtsp_message_init_request (msg, method, urlstr) != RTSP_OK) + res = RTSP_EPARSE; + } else if (strncmp (versionstr, "RTSP/", 5) == 0) { + if (rtsp_message_init_request (msg, method, urlstr) != RTSP_OK) + res = RTSP_EPARSE; + msg->type_data.request.version = RTSP_VERSION_INVALID; + } else { + rtsp_message_init_request (msg, method, urlstr); + msg->type_data.request.version = RTSP_VERSION_INVALID; + res = RTSP_EPARSE; } + + return res; } /* parsing lines means reading a Key: Value pair */ @@ -667,7 +691,7 @@ parse_line (gchar * buffer, RTSPMessage * msg) no_column: { - return RTSP_EINVAL; + return RTSP_EPARSE; } } @@ -821,7 +845,6 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg, { gchar buffer[4096]; gint line; - gchar *hdrval; glong content_length; RTSPResult res; gboolean need_body; @@ -899,6 +922,9 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg, /* read the rest of the body if needed */ if (need_body) { + gchar *session_id; + gchar *hdrval; + /* see if there is a Content-Length header */ if (rtsp_message_get_header (msg, RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == RTSP_OK) { @@ -908,38 +934,37 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg, } /* save session id in the connection for further use */ - { - gchar *session_id; + if (rtsp_message_get_header (msg, RTSP_HDR_SESSION, + &session_id, 0) == RTSP_OK) { + gint maxlen, i; - if (rtsp_message_get_header (msg, RTSP_HDR_SESSION, - &session_id, 0) == RTSP_OK) { - gint sesslen, maxlen, i; + /* default session timeout */ + conn->timeout = 60; - /* default session timeout */ - conn->timeout = 60; + maxlen = sizeof (conn->session_id) - 1; + /* the sessionid can have attributes marked with ; + * Make sure we strip them */ + for (i = 0; session_id[i] != '\0'; i++) { + if (session_id[i] == ';') { + maxlen = i; + /* parse timeout */ + do { + i++; + } while (g_ascii_isspace (session_id[i])); + if (g_str_has_prefix (&session_id[i], "timeout=")) { + gint to; - sesslen = strlen (session_id); - maxlen = sizeof (conn->session_id) - 1; - /* the sessionid can have attributes marked with ; - * Make sure we strip them */ - for (i = 0; i < sesslen; i++) { - if (session_id[i] == ';') { - maxlen = i; - /* parse timeout */ - if (g_str_has_prefix (&session_id[i], ";timeout=")) { - gint timeout; - - /* if we parsed something valid, configure */ - if ((timeout = atoi (&session_id[i + 9])) > 0) - conn->timeout = timeout; - } + /* if we parsed something valid, configure */ + if ((to = atoi (&session_id[i + 9])) > 0) + conn->timeout = to; } + break; } - - /* make sure to not overflow */ - strncpy (conn->session_id, session_id, maxlen); - conn->session_id[maxlen] = '\0'; } + + /* make sure to not overflow */ + strncpy (conn->session_id, session_id, maxlen); + conn->session_id[maxlen] = '\0'; } } return res; diff --git a/gst/rtsp/rtspdefs.c b/gst/rtsp/rtspdefs.c index 2c3f7b73bf..8f11a5d981 100644 --- a/gst/rtsp/rtspdefs.c +++ b/gst/rtsp/rtspdefs.c @@ -254,6 +254,18 @@ rtsp_method_as_text (RTSPMethod method) return rtsp_methods[i]; } +const gchar * +rtsp_version_as_text (RTSPVersion version) +{ + switch (version) { + case RTSP_VERSION_1_0: + return "1.0"; + + default: + return "0.0"; + } +} + const gchar * rtsp_header_as_text (RTSPHeaderField field) { diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h index 8a2182db90..2045c1fcb1 100644 --- a/gst/rtsp/rtspdefs.h +++ b/gst/rtsp/rtspdefs.h @@ -89,6 +89,11 @@ typedef enum { RTSP_STATE_RECORDING, } RTSPState; +typedef enum { + RTSP_VERSION_INVALID = 0x00, + RTSP_VERSION_1_0 = 0x10, +} RTSPVersion; + typedef enum { RTSP_INVALID = 0, RTSP_DESCRIBE = (1 << 0), @@ -232,6 +237,7 @@ typedef enum { gchar* rtsp_strresult (RTSPResult result); const gchar* rtsp_method_as_text (RTSPMethod method); +const gchar* rtsp_version_as_text (RTSPVersion version); const gchar* rtsp_header_as_text (RTSPHeaderField field); const gchar* rtsp_status_as_text (RTSPStatusCode code); diff --git a/gst/rtsp/rtspmessage.c b/gst/rtsp/rtspmessage.c index 2cb12bf3e1..5dd2faabe4 100644 --- a/gst/rtsp/rtspmessage.c +++ b/gst/rtsp/rtspmessage.c @@ -118,6 +118,7 @@ rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, msg->type = RTSP_MESSAGE_REQUEST; msg->type_data.request.method = method; msg->type_data.request.uri = g_strdup (uri); + msg->type_data.request.version = RTSP_VERSION_1_0; msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue)); return RTSP_OK; @@ -152,6 +153,7 @@ rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code, msg->type = RTSP_MESSAGE_RESPONSE; msg->type_data.response.code = code; msg->type_data.response.reason = g_strdup (reason); + msg->type_data.response.version = RTSP_VERSION_1_0; msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue)); if (request) { @@ -424,9 +426,11 @@ rtsp_message_dump (RTSPMessage * msg) case RTSP_MESSAGE_REQUEST: g_print ("RTSP request message %p\n", msg); g_print (" request line:\n"); - g_print (" method: '%s'\n", + g_print (" method: '%s'\n", rtsp_method_as_text (msg->type_data.request.method)); - g_print (" uri: '%s'\n", msg->type_data.request.uri); + g_print (" uri: '%s'\n", msg->type_data.request.uri); + g_print (" version: '%s'\n", + rtsp_version_as_text (msg->type_data.request.version)); g_print (" headers:\n"); key_value_foreach (msg->hdr_fields, dump_key_value, NULL); g_print (" body:\n"); @@ -436,8 +440,10 @@ rtsp_message_dump (RTSPMessage * msg) case RTSP_MESSAGE_RESPONSE: g_print ("RTSP response message %p\n", msg); g_print (" status line:\n"); - g_print (" code: '%d'\n", msg->type_data.response.code); - g_print (" reason: '%s'\n", msg->type_data.response.reason); + g_print (" code: '%d'\n", msg->type_data.response.code); + g_print (" reason: '%s'\n", msg->type_data.response.reason); + g_print (" version: '%s'\n", + rtsp_version_as_text (msg->type_data.response.version)); g_print (" headers:\n"); key_value_foreach (msg->hdr_fields, dump_key_value, NULL); rtsp_message_get_body (msg, &data, &size); diff --git a/gst/rtsp/rtspmessage.h b/gst/rtsp/rtspmessage.h index 2c099f17ff..15d173c37c 100644 --- a/gst/rtsp/rtspmessage.h +++ b/gst/rtsp/rtspmessage.h @@ -65,10 +65,12 @@ typedef struct _RTSPMessage struct { RTSPMethod method; gchar *uri; + RTSPVersion version; } request; struct { RTSPStatusCode code; gchar *reason; + RTSPVersion version; } response; struct { guint8 channel;