mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
rtsp: Improved parsing of messages.
Do not abort message parsing as soon as there is an error. Instead parse as much as possible to allow a server to return as meaningful an error as possible.
This commit is contained in:
parent
ca154010fe
commit
fdd5a65632
1 changed files with 178 additions and 129 deletions
|
@ -222,6 +222,7 @@ enum
|
|||
typedef struct
|
||||
{
|
||||
gint state;
|
||||
GstRTSPResult status;
|
||||
guint8 buffer[4096];
|
||||
guint offset;
|
||||
|
||||
|
@ -1510,10 +1511,54 @@ parse_key (gchar * dest, gint size, gchar ** src)
|
|||
dest[idx] = '\0';
|
||||
}
|
||||
|
||||
static GstRTSPResult
|
||||
parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
|
||||
GstRTSPVersion * version)
|
||||
{
|
||||
GstRTSPResult res = GST_RTSP_OK;
|
||||
gchar *ver;
|
||||
|
||||
if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
|
||||
guint major;
|
||||
guint minor;
|
||||
gchar dummychar;
|
||||
|
||||
*ver++ = '\0';
|
||||
|
||||
/* the version number must be formatted as X.Y with nothing following */
|
||||
if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
|
||||
if (major != 1 || minor != 0) {
|
||||
*version = GST_RTSP_VERSION_INVALID;
|
||||
res = GST_RTSP_ERROR;
|
||||
}
|
||||
} else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
|
||||
if (*type == GST_RTSP_MESSAGE_REQUEST)
|
||||
*type = GST_RTSP_MESSAGE_HTTP_REQUEST;
|
||||
else if (*type == GST_RTSP_MESSAGE_RESPONSE)
|
||||
*type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
|
||||
|
||||
if (major == 1 && minor == 1) {
|
||||
*version = GST_RTSP_VERSION_1_1;
|
||||
} else if (major != 1 || minor != 0) {
|
||||
*version = GST_RTSP_VERSION_INVALID;
|
||||
res = GST_RTSP_ERROR;
|
||||
}
|
||||
} else
|
||||
res = GST_RTSP_EPARSE;
|
||||
} else
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstRTSPResult
|
||||
parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
|
||||
{
|
||||
GstRTSPResult res;
|
||||
GstRTSPResult res = GST_RTSP_OK;
|
||||
GstRTSPResult res2;
|
||||
gchar versionstr[20];
|
||||
gchar codestr[4];
|
||||
gint code;
|
||||
|
@ -1522,102 +1567,80 @@ parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
|
|||
bptr = (gchar *) buffer;
|
||||
|
||||
parse_string (versionstr, sizeof (versionstr), &bptr);
|
||||
|
||||
parse_string (codestr, sizeof (codestr), &bptr);
|
||||
code = atoi (codestr);
|
||||
if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
while (g_ascii_isspace (*bptr))
|
||||
bptr++;
|
||||
|
||||
if (strcmp (versionstr, "RTSP/1.0") == 0)
|
||||
GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
|
||||
parse_error);
|
||||
else if (strncmp (versionstr, "RTSP/", 5) == 0) {
|
||||
GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
|
||||
parse_error);
|
||||
msg->type_data.response.version = GST_RTSP_VERSION_INVALID;
|
||||
} else
|
||||
goto parse_error;
|
||||
if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
|
||||
NULL) != GST_RTSP_OK))
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
return GST_RTSP_OK;
|
||||
res2 = parse_protocol_version (versionstr, &msg->type,
|
||||
&msg->type_data.response.version);
|
||||
if (G_LIKELY (res == GST_RTSP_OK))
|
||||
res = res2;
|
||||
|
||||
parse_error:
|
||||
{
|
||||
return GST_RTSP_EPARSE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstRTSPResult
|
||||
parse_request_line (GstRTSPConnection * conn, guint8 * buffer,
|
||||
GstRTSPMessage * msg)
|
||||
parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
|
||||
{
|
||||
GstRTSPResult res = GST_RTSP_OK;
|
||||
GstRTSPResult res2;
|
||||
gchar versionstr[20];
|
||||
gchar methodstr[20];
|
||||
gchar urlstr[4096];
|
||||
gchar *bptr;
|
||||
GstRTSPMethod method;
|
||||
GstRTSPTunnelState tstate = TUNNEL_STATE_NONE;
|
||||
|
||||
bptr = (gchar *) buffer;
|
||||
|
||||
parse_string (methodstr, sizeof (methodstr), &bptr);
|
||||
method = gst_rtsp_find_method (methodstr);
|
||||
if (method == GST_RTSP_INVALID) {
|
||||
/* a tunnel request is allowed when we don't have one yet */
|
||||
if (conn->tstate != TUNNEL_STATE_NONE)
|
||||
goto invalid_method;
|
||||
/* we need GET or POST for a valid tunnel request */
|
||||
if (!strcmp (methodstr, "GET"))
|
||||
tstate = TUNNEL_STATE_GET;
|
||||
else if (!strcmp (methodstr, "POST"))
|
||||
tstate = TUNNEL_STATE_POST;
|
||||
else
|
||||
goto invalid_method;
|
||||
}
|
||||
|
||||
parse_string (urlstr, sizeof (urlstr), &bptr);
|
||||
if (G_UNLIKELY (*urlstr == '\0'))
|
||||
goto invalid_url;
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
parse_string (versionstr, sizeof (versionstr), &bptr);
|
||||
|
||||
if (G_UNLIKELY (*bptr != '\0'))
|
||||
goto invalid_version;
|
||||
|
||||
if (strcmp (versionstr, "RTSP/1.0") == 0) {
|
||||
res = gst_rtsp_message_init_request (msg, method, urlstr);
|
||||
} else if (strncmp (versionstr, "RTSP/", 5) == 0) {
|
||||
res = gst_rtsp_message_init_request (msg, method, urlstr);
|
||||
msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
|
||||
} else if (strcmp (versionstr, "HTTP/1.0") == 0) {
|
||||
/* tunnel request, we need a tunnel method */
|
||||
if (tstate == TUNNEL_STATE_NONE) {
|
||||
res = GST_RTSP_EPARSE;
|
||||
} else {
|
||||
conn->tstate = tstate;
|
||||
}
|
||||
} else {
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
|
||||
urlstr) != GST_RTSP_OK))
|
||||
res = GST_RTSP_EPARSE;
|
||||
|
||||
res2 = parse_protocol_version (versionstr, &msg->type,
|
||||
&msg->type_data.request.version);
|
||||
if (G_LIKELY (res == GST_RTSP_OK))
|
||||
res = res2;
|
||||
|
||||
if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
|
||||
/* GET and POST are not allowed as RTSP methods */
|
||||
if (msg->type_data.request.method == GST_RTSP_GET ||
|
||||
msg->type_data.request.method == GST_RTSP_POST) {
|
||||
msg->type_data.request.method = GST_RTSP_INVALID;
|
||||
if (res == GST_RTSP_OK)
|
||||
res = GST_RTSP_ERROR;
|
||||
}
|
||||
} else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
|
||||
/* only GET and POST are allowed as HTTP methods */
|
||||
if (msg->type_data.request.method != GST_RTSP_GET &&
|
||||
msg->type_data.request.method != GST_RTSP_POST) {
|
||||
msg->type_data.request.method = GST_RTSP_INVALID;
|
||||
if (res == GST_RTSP_OK)
|
||||
res = GST_RTSP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
/* ERRORS */
|
||||
invalid_method:
|
||||
{
|
||||
GST_ERROR ("invalid method %s", methodstr);
|
||||
return GST_RTSP_EPARSE;
|
||||
}
|
||||
invalid_url:
|
||||
{
|
||||
GST_ERROR ("invalid url %s", urlstr);
|
||||
return GST_RTSP_EPARSE;
|
||||
}
|
||||
invalid_version:
|
||||
{
|
||||
GST_ERROR ("invalid version");
|
||||
return GST_RTSP_EPARSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstRTSPResult
|
||||
|
@ -1649,7 +1672,7 @@ no_column:
|
|||
|
||||
/* parsing lines means reading a Key: Value pair */
|
||||
static GstRTSPResult
|
||||
parse_line (GstRTSPConnection * conn, guint8 * buffer, GstRTSPMessage * msg)
|
||||
parse_line (guint8 * buffer, GstRTSPMessage * msg)
|
||||
{
|
||||
GstRTSPResult res;
|
||||
gchar key[32];
|
||||
|
@ -1660,18 +1683,9 @@ parse_line (GstRTSPConnection * conn, guint8 * buffer, GstRTSPMessage * msg)
|
|||
if (G_UNLIKELY (res != GST_RTSP_OK))
|
||||
goto parse_error;
|
||||
|
||||
if (conn->tstate == TUNNEL_STATE_GET || conn->tstate == TUNNEL_STATE_POST) {
|
||||
/* save the tunnel session in the connection */
|
||||
if (!strcmp (key, "x-sessioncookie")) {
|
||||
strncpy (conn->tunnelid, value, TUNNELID_LEN);
|
||||
conn->tunnelid[TUNNELID_LEN - 1] = '\0';
|
||||
conn->tunneled = TRUE;
|
||||
}
|
||||
} else {
|
||||
field = gst_rtsp_find_header_field (key);
|
||||
if (field != GST_RTSP_HDR_INVALID)
|
||||
gst_rtsp_message_add_header (msg, field, value);
|
||||
}
|
||||
field = gst_rtsp_find_header_field (key);
|
||||
if (field != GST_RTSP_HDR_INVALID)
|
||||
gst_rtsp_message_add_header (msg, field, value);
|
||||
|
||||
return GST_RTSP_OK;
|
||||
|
||||
|
@ -1781,17 +1795,17 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
|
|||
/* we have a line */
|
||||
if (builder->line == 0) {
|
||||
/* first line, check for response status */
|
||||
if (memcmp (builder->buffer, "RTSP", 4) == 0) {
|
||||
res = parse_response_status (builder->buffer, message);
|
||||
if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
|
||||
memcmp (builder->buffer, "HTTP", 4) == 0) {
|
||||
builder->status = parse_response_status (builder->buffer, message);
|
||||
} else {
|
||||
res = parse_request_line (conn, builder->buffer, message);
|
||||
builder->status = parse_request_line (builder->buffer, message);
|
||||
}
|
||||
/* the first line must parse without errors */
|
||||
if (res != GST_RTSP_OK)
|
||||
goto done;
|
||||
} else {
|
||||
/* else just parse the line, ignore errors */
|
||||
parse_line (conn, builder->buffer, message);
|
||||
/* else just parse the line */
|
||||
res = parse_line (builder->buffer, message);
|
||||
if (res != GST_RTSP_OK)
|
||||
builder->status = res;
|
||||
}
|
||||
builder->line++;
|
||||
builder->offset = 0;
|
||||
|
@ -1799,22 +1813,26 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
|
|||
}
|
||||
case STATE_END:
|
||||
{
|
||||
gchar *session_cookie;
|
||||
gchar *session_id;
|
||||
|
||||
if (conn->tstate == TUNNEL_STATE_GET) {
|
||||
res = GST_RTSP_ETGET;
|
||||
goto done;
|
||||
} else if (conn->tstate == TUNNEL_STATE_POST) {
|
||||
res = GST_RTSP_ETPOST;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (message->type == GST_RTSP_MESSAGE_DATA) {
|
||||
/* data messages don't have headers */
|
||||
res = GST_RTSP_OK;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* save the tunnel session in the connection */
|
||||
if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
|
||||
!conn->manual_http &&
|
||||
conn->tstate == TUNNEL_STATE_NONE &&
|
||||
gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
|
||||
&session_cookie, 0) == GST_RTSP_OK) {
|
||||
strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
|
||||
conn->tunnelid[TUNNELID_LEN - 1] = '\0';
|
||||
conn->tunneled = TRUE;
|
||||
}
|
||||
|
||||
/* save session id in the connection for further use */
|
||||
if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
|
||||
gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
|
||||
|
@ -1846,7 +1864,7 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
|
|||
strncpy (conn->session_id, session_id, maxlen);
|
||||
conn->session_id[maxlen] = '\0';
|
||||
}
|
||||
res = GST_RTSP_OK;
|
||||
res = builder->status;
|
||||
goto done;
|
||||
}
|
||||
default:
|
||||
|
@ -2021,20 +2039,36 @@ gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
|
|||
res = build_next (&builder, message, conn);
|
||||
if (G_UNLIKELY (res == GST_RTSP_EEOF))
|
||||
goto eof;
|
||||
if (G_LIKELY (res == GST_RTSP_OK))
|
||||
break;
|
||||
if (res == GST_RTSP_ETGET) {
|
||||
GString *str;
|
||||
else if (G_LIKELY (res == GST_RTSP_OK)) {
|
||||
if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
|
||||
if (conn->tstate == TUNNEL_STATE_NONE &&
|
||||
message->type_data.request.method == GST_RTSP_GET) {
|
||||
GString *str;
|
||||
|
||||
conn->tstate = TUNNEL_STATE_GET;
|
||||
|
||||
/* tunnel GET request, we can reply now */
|
||||
str = gen_tunnel_reply (conn, GST_RTSP_STS_OK);
|
||||
res =
|
||||
gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len,
|
||||
timeout);
|
||||
g_string_free (str, TRUE);
|
||||
res = GST_RTSP_ETGET;
|
||||
goto cleanup;
|
||||
} else if (conn->tstate == TUNNEL_STATE_NONE &&
|
||||
message->type_data.request.method == GST_RTSP_POST) {
|
||||
conn->tstate = TUNNEL_STATE_POST;
|
||||
|
||||
/* tunnel POST request, the caller now has to link the two
|
||||
* connections. */
|
||||
res = GST_RTSP_ETPOST;
|
||||
goto cleanup;
|
||||
} else {
|
||||
res = GST_RTSP_EPARSE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* tunnel GET request, we can reply now */
|
||||
str = gen_tunnel_reply (conn, GST_RTSP_STS_OK);
|
||||
res =
|
||||
gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len,
|
||||
timeout);
|
||||
g_string_free (str, TRUE);
|
||||
} else if (res == GST_RTSP_ETPOST) {
|
||||
/* tunnel POST request, return the value, the caller now has to link the
|
||||
* two connections. */
|
||||
break;
|
||||
} else if (G_UNLIKELY (res != GST_RTSP_EINTR))
|
||||
goto read_error;
|
||||
|
@ -2836,38 +2870,53 @@ gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
|
|||
res = build_next (&watch->builder, &watch->message, watch->conn);
|
||||
if (res == GST_RTSP_EINTR)
|
||||
break;
|
||||
if (G_UNLIKELY (res == GST_RTSP_EEOF))
|
||||
else if (G_UNLIKELY (res == GST_RTSP_EEOF))
|
||||
goto eof;
|
||||
if (res == GST_RTSP_ETGET) {
|
||||
GString *str;
|
||||
GstRTSPStatusCode code;
|
||||
guint size;
|
||||
else if (G_LIKELY (res == GST_RTSP_OK)) {
|
||||
if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
|
||||
if (watch->conn->tstate == TUNNEL_STATE_NONE &&
|
||||
watch->message.type_data.request.method == GST_RTSP_GET) {
|
||||
GString *str;
|
||||
GstRTSPStatusCode code;
|
||||
guint size;
|
||||
|
||||
if (watch->funcs.tunnel_start)
|
||||
code = watch->funcs.tunnel_start (watch, watch->user_data);
|
||||
else
|
||||
code = GST_RTSP_STS_OK;
|
||||
watch->conn->tstate = TUNNEL_STATE_GET;
|
||||
|
||||
/* queue the response string */
|
||||
str = gen_tunnel_reply (watch->conn, code);
|
||||
size = str->len;
|
||||
gst_rtsp_watch_queue_data (watch, (guint8 *) g_string_free (str, FALSE),
|
||||
size);
|
||||
} else if (res == GST_RTSP_ETPOST) {
|
||||
/* in the callback the connection should be tunneled with the
|
||||
* GET connection */
|
||||
if (watch->funcs.tunnel_complete)
|
||||
watch->funcs.tunnel_complete (watch, watch->user_data);
|
||||
} else if (G_UNLIKELY (res != GST_RTSP_OK))
|
||||
goto error;
|
||||
if (watch->funcs.tunnel_start)
|
||||
code = watch->funcs.tunnel_start (watch, watch->user_data);
|
||||
else
|
||||
code = GST_RTSP_STS_OK;
|
||||
|
||||
/* queue the response string */
|
||||
str = gen_tunnel_reply (watch->conn, code);
|
||||
size = str->len;
|
||||
gst_rtsp_watch_queue_data (watch, (guint8 *) g_string_free (str,
|
||||
FALSE), size);
|
||||
goto read_done;
|
||||
} else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
|
||||
watch->message.type_data.request.method == GST_RTSP_POST) {
|
||||
watch->conn->tstate = TUNNEL_STATE_POST;
|
||||
|
||||
/* in the callback the connection should be tunneled with the
|
||||
* GET connection */
|
||||
if (watch->funcs.tunnel_complete)
|
||||
watch->funcs.tunnel_complete (watch, watch->user_data);
|
||||
goto read_done;
|
||||
} else {
|
||||
res = GST_RTSP_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (G_LIKELY (res == GST_RTSP_OK)) {
|
||||
if (watch->funcs.message_received)
|
||||
watch->funcs.message_received (watch, &watch->message,
|
||||
watch->user_data);
|
||||
} else
|
||||
goto error;
|
||||
|
||||
gst_rtsp_message_unset (&watch->message);
|
||||
}
|
||||
read_done:
|
||||
gst_rtsp_message_unset (&watch->message);
|
||||
build_reset (&watch->builder);
|
||||
} while (FALSE);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue