2 * Copyright (C) <2005-2009> Wim Taymans <wim.taymans@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
24 * Permission is hereby granted, free of charge, to any person obtaining a copy of
25 * this software and associated documentation files (the "Software"), to deal in
26 * the Software without restriction, including without limitation the rights to
27 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28 * of the Software, and to permit persons to whom the Software is furnished to do
29 * so, subject to the following conditions:
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44 * SECTION:gstrtspconnection
45 * @short_description: manage RTSP connections
46 * @see_also: gstrtspurl
48 * This object manages the RTSP connection to the server. It provides function
49 * to receive and send bytes and messages.
51 * Last reviewed on 2007-07-24 (0.10.14)
64 /* we include this here to get the G_OS_* defines */
68 #include "gstrtspconnection.h"
70 #include "gst/glib-compat-private.h"
76 struct sockaddr_in sa_in;
77 struct sockaddr_in6 sa_in6;
78 struct sockaddr_storage sa_stor;
86 guchar out[3]; /* the size must be evenly divisible by 3 */
92 #define SEND_FLAGS MSG_NOSIGNAL
102 TUNNEL_STATE_COMPLETE
103 } GstRTSPTunnelState;
105 #define TUNNELID_LEN 24
107 struct _GstRTSPConnection
110 /* URL for the connection */
113 /* connection state */
114 GSocket *read_socket;
115 GSocket *write_socket;
116 gboolean manual_http;
117 GSocket *socket0, *socket1;
118 GCancellable *cancellable;
120 gchar tunnelid[TUNNELID_LEN];
122 GstRTSPTunnelState tstate;
128 gchar *initial_buffer;
129 gsize initial_buffer_offset;
132 gint cseq; /* sequence number */
133 gchar session_id[512]; /* session id */
134 gint timeout; /* session timeout in seconds */
135 GTimer *timer; /* timeout timer */
138 GstRTSPAuthMethod auth_method;
141 GHashTable *auth_params;
162 READ_AHEAD_EOH = -1, /* end of headers */
163 READ_AHEAD_CRLF = -2,
164 READ_AHEAD_CRLFCR = -3
167 /* a structure for constructing RTSPMessages */
171 GstRTSPResult status;
181 build_reset (GstRTSPBuilder * builder)
183 g_free (builder->body_data);
184 memset (builder, 0, sizeof (GstRTSPBuilder));
188 * gst_rtsp_connection_create:
189 * @url: a #GstRTSPUrl
190 * @conn: storage for a #GstRTSPConnection
192 * Create a newly allocated #GstRTSPConnection from @url and store it in @conn.
193 * The connection will not yet attempt to connect to @url, use
194 * gst_rtsp_connection_connect().
196 * A copy of @url will be made.
198 * Returns: #GST_RTSP_OK when @conn contains a valid connection.
201 gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
203 GstRTSPConnection *newconn;
205 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
207 newconn = g_new0 (GstRTSPConnection, 1);
209 newconn->cancellable = g_cancellable_new ();
211 newconn->url = gst_rtsp_url_copy (url);
212 newconn->timer = g_timer_new ();
213 newconn->timeout = 60;
216 newconn->auth_method = GST_RTSP_AUTH_NONE;
217 newconn->username = NULL;
218 newconn->passwd = NULL;
219 newconn->auth_params = NULL;
227 * gst_rtsp_connection_create_from_socket:
228 * @socket: a #GSocket
229 * @ip: the IP address of the other end
230 * @port: the port used by the other end
231 * @initial_buffer: data already read from @fd
232 * @conn: storage for a #GstRTSPConnection
234 * Create a new #GstRTSPConnection for handling communication on the existing
235 * socket @socket. The @initial_buffer contains any data already read from
236 * @socket which should be used before starting to read new data.
238 * Returns: #GST_RTSP_OK when @conn contains a valid connection.
243 gst_rtsp_connection_create_from_socket (GSocket * socket, const gchar * ip,
244 guint16 port, const gchar * initial_buffer, GstRTSPConnection ** conn)
246 GstRTSPConnection *newconn = NULL;
250 g_return_val_if_fail (G_IS_SOCKET (socket), GST_RTSP_EINVAL);
251 g_return_val_if_fail (ip != NULL, GST_RTSP_EINVAL);
252 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
254 /* set to non-blocking mode so that we can cancel the communication */
255 g_socket_set_blocking (socket, FALSE);
257 /* create a url for the client address */
258 url = g_new0 (GstRTSPUrl, 1);
259 url->host = g_strdup (ip);
262 /* now create the connection object */
263 GST_RTSP_CHECK (gst_rtsp_connection_create (url, &newconn), newconn_failed);
264 gst_rtsp_url_free (url);
266 /* both read and write initially */
267 newconn->socket0 = G_SOCKET (g_object_ref (socket));
268 newconn->socket1 = G_SOCKET (g_object_ref (socket));
269 newconn->write_socket = newconn->read_socket = newconn->socket0;
271 newconn->ip = g_strdup (ip);
273 newconn->initial_buffer = g_strdup (initial_buffer);
282 gst_rtsp_url_free (url);
288 * gst_rtsp_connection_accept:
290 * @conn: storage for a #GstRTSPConnection
291 * @cancellable: a #GCancellable to cancel the operation
293 * Accept a new connection on @socket and create a new #GstRTSPConnection for
294 * handling communication on new socket.
296 * Returns: #GST_RTSP_OK when @conn contains a valid connection.
301 gst_rtsp_connection_accept (GSocket * socket, GstRTSPConnection ** conn,
302 GCancellable * cancellable)
307 GSocket *client_sock;
308 GSocketAddress *addr;
311 g_return_val_if_fail (G_IS_SOCKET (socket), GST_RTSP_EINVAL);
312 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
314 client_sock = g_socket_accept (socket, cancellable, &err);
318 addr = g_socket_get_remote_address (client_sock, &err);
320 goto getnameinfo_failed;
322 ip = g_inet_address_to_string (g_inet_socket_address_get_address
323 (G_INET_SOCKET_ADDRESS (addr)));
324 port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
327 gst_rtsp_connection_create_from_socket (client_sock, ip, port, NULL,
336 GST_DEBUG ("Accepting client failed: %s", err->message);
337 g_clear_error (&err);
338 return GST_RTSP_ESYS;
342 if (!g_socket_close (client_sock, &err)) {
343 GST_DEBUG ("Closing socket failed: %s", err->message);
344 g_clear_error (&err);
346 g_object_unref (client_sock);
347 return GST_RTSP_ERROR;
352 do_resolve (const gchar * host, GCancellable * cancellable)
359 addr = g_inet_address_new_from_string (host);
363 resolver = g_resolver_get_default ();
365 results = g_resolver_lookup_by_name (resolver, host, cancellable, &err);
369 for (l = results; l; l = l->next) {
370 GInetAddress *tmp = l->data;
372 if (g_inet_address_get_family (tmp) == G_SOCKET_FAMILY_IPV4 ||
373 g_inet_address_get_family (tmp) == G_SOCKET_FAMILY_IPV6) {
374 addr = G_INET_ADDRESS (g_object_ref (tmp));
379 g_resolver_free_addresses (results);
380 g_object_unref (resolver);
386 ip = g_inet_address_to_string (addr);
387 g_object_unref (addr);
394 GST_ERROR ("failed to resolve %s: %s", host, err->message);
395 g_clear_error (&err);
396 g_object_unref (resolver);
402 do_connect (const gchar * ip, guint16 port, GSocket ** socket_out,
403 GTimeVal * timeout, GCancellable * cancellable)
408 GSocketAddress *saddr;
411 addr = g_inet_address_new_from_string (ip);
413 saddr = g_inet_socket_address_new (addr, port);
414 g_object_unref (addr);
417 g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM,
418 G_SOCKET_PROTOCOL_TCP, &err);
422 /* set to non-blocking mode so that we can cancel the connect */
423 g_socket_set_blocking (socket, FALSE);
425 /* we are going to connect ASYNC now */
426 if (!g_socket_connect (socket, saddr, cancellable, &err)) {
427 if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_PENDING))
433 /* wait for connect to complete up to the specified timeout or until we got
435 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
437 g_socket_set_timeout (socket, (to + GST_SECOND - 1) / GST_SECOND);
438 if (!g_socket_condition_wait (socket, G_IO_OUT, cancellable, &err)) {
439 g_socket_set_timeout (socket, 0);
440 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
445 g_socket_set_timeout (socket, 0);
447 if (g_socket_check_connect_result (socket, &err))
451 g_object_unref (saddr);
458 GST_ERROR ("no socket: %s", err->message);
459 g_clear_error (&err);
460 g_object_unref (saddr);
461 return GST_RTSP_ESYS;
465 GST_ERROR ("system error: %s", err->message);
466 g_clear_error (&err);
467 g_object_unref (saddr);
468 g_object_unref (socket);
469 return GST_RTSP_ESYS;
473 GST_ERROR ("timeout");
474 g_clear_error (&err);
475 g_object_unref (saddr);
476 g_object_unref (socket);
477 return GST_RTSP_ETIMEOUT;
482 setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
489 guint16 port, url_port;
493 GstRTSPMessage response;
496 memset (&response, 0, sizeof (response));
497 gst_rtsp_message_init (&response);
499 /* create a random sessionid */
500 for (i = 0; i < TUNNELID_LEN; i++)
501 conn->tunnelid[i] = g_random_int_range ('a', 'z');
502 conn->tunnelid[TUNNELID_LEN - 1] = '\0';
505 /* get the port from the url */
506 gst_rtsp_url_get_port (url, &url_port);
508 if (conn->proxy_host) {
509 uri = g_strdup_printf ("http://%s:%d%s%s%s", url->host, url_port,
510 url->abspath, url->query ? "?" : "", url->query ? url->query : "");
511 hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
512 ip = conn->proxy_host;
513 port = conn->proxy_port;
515 uri = g_strdup_printf ("%s%s%s", url->abspath, url->query ? "?" : "",
516 url->query ? url->query : "");
522 /* create the GET request for the read connection */
523 GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_GET, uri),
525 msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
527 if (hostparam != NULL)
528 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
529 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
531 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
532 "application/x-rtsp-tunnelled");
533 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
534 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
536 /* we start by writing to this fd */
537 conn->write_socket = conn->socket0;
539 /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
540 * request from being base64 encoded */
541 conn->tunneled = FALSE;
542 GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
543 gst_rtsp_message_free (msg);
544 conn->tunneled = TRUE;
546 /* receive the response to the GET request */
547 /* we need to temporarily set manual_http to TRUE since
548 * gst_rtsp_connection_receive() will treat the HTTP response as a parsing
549 * failure otherwise */
550 old_http = conn->manual_http;
551 conn->manual_http = TRUE;
552 GST_RTSP_CHECK (gst_rtsp_connection_receive (conn, &response, timeout),
554 conn->manual_http = old_http;
556 if (response.type != GST_RTSP_MESSAGE_HTTP_RESPONSE ||
557 response.type_data.response.code != GST_RTSP_STS_OK)
560 if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
561 &value, 0) == GST_RTSP_OK) {
562 if (conn->proxy_host) {
563 /* if we use a proxy we need to change the destination url */
565 url->host = g_strdup (value);
567 hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
569 /* and resolve the new ip address */
570 if (!(ip = do_resolve (value, conn->cancellable)))
577 /* connect to the host/port */
578 res = do_connect (ip, port, &conn->socket1, timeout, conn->cancellable);
579 if (res != GST_RTSP_OK)
582 /* this is now our writing socket */
583 conn->write_socket = conn->socket1;
585 /* create the POST request for the write connection */
586 GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_POST, uri),
588 msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
590 if (hostparam != NULL)
591 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
592 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
594 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
595 "application/x-rtsp-tunnelled");
596 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
597 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
598 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_EXPIRES,
599 "Sun, 9 Jan 1972 00:00:00 GMT");
600 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, "32767");
602 /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
603 * request from being base64 encoded */
604 conn->tunneled = FALSE;
605 GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
606 gst_rtsp_message_free (msg);
607 conn->tunneled = TRUE;
610 gst_rtsp_message_unset (&response);
619 GST_ERROR ("failed to create request (%d)", res);
624 GST_ERROR ("write failed (%d)", res);
625 gst_rtsp_message_free (msg);
626 conn->tunneled = TRUE;
631 GST_ERROR ("read failed (%d)", res);
632 conn->manual_http = FALSE;
637 GST_ERROR ("got failure response %d %s", response.type_data.response.code,
638 response.type_data.response.reason);
639 res = GST_RTSP_ERROR;
644 GST_ERROR ("could not resolve %s", conn->ip);
650 GST_ERROR ("failed to connect");
656 * gst_rtsp_connection_connect:
657 * @conn: a #GstRTSPConnection
658 * @timeout: a #GTimeVal timeout
660 * Attempt to connect to the url of @conn made with
661 * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
662 * forever. If @timeout contains a valid timeout, this function will return
663 * #GST_RTSP_ETIMEOUT after the timeout expired.
665 * This function can be cancelled with gst_rtsp_connection_flush().
667 * Returns: #GST_RTSP_OK when a connection could be made.
670 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
677 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
678 g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
679 g_return_val_if_fail (conn->socket0 == NULL, GST_RTSP_EINVAL);
683 if (conn->proxy_host && conn->tunneled) {
684 if (!(ip = do_resolve (conn->proxy_host, conn->cancellable))) {
685 GST_ERROR ("could not resolve %s", conn->proxy_host);
688 port = conn->proxy_port;
689 g_free (conn->proxy_host);
690 conn->proxy_host = ip;
692 if (!(ip = do_resolve (url->host, conn->cancellable))) {
693 GST_ERROR ("could not resolve %s", url->host);
696 /* get the port from the url */
697 gst_rtsp_url_get_port (url, &port);
703 /* connect to the host/port */
704 res = do_connect (ip, port, &conn->socket0, timeout, conn->cancellable);
705 if (res != GST_RTSP_OK)
708 /* this is our read URL */
709 conn->read_socket = conn->socket0;
711 if (conn->tunneled) {
712 res = setup_tunneling (conn, timeout);
713 if (res != GST_RTSP_OK)
714 goto tunneling_failed;
716 conn->write_socket = conn->socket0;
723 return GST_RTSP_ENET;
727 GST_ERROR ("failed to connect");
732 GST_ERROR ("failed to setup tunneling");
738 auth_digest_compute_hex_urp (const gchar * username,
739 const gchar * realm, const gchar * password, gchar hex_urp[33])
741 GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
742 const gchar *digest_string;
744 g_checksum_update (md5_context, (const guchar *) username, strlen (username));
745 g_checksum_update (md5_context, (const guchar *) ":", 1);
746 g_checksum_update (md5_context, (const guchar *) realm, strlen (realm));
747 g_checksum_update (md5_context, (const guchar *) ":", 1);
748 g_checksum_update (md5_context, (const guchar *) password, strlen (password));
749 digest_string = g_checksum_get_string (md5_context);
751 memset (hex_urp, 0, 33);
752 memcpy (hex_urp, digest_string, strlen (digest_string));
754 g_checksum_free (md5_context);
758 auth_digest_compute_response (const gchar * method,
759 const gchar * uri, const gchar * hex_a1, const gchar * nonce,
762 char hex_a2[33] = { 0, };
763 GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
764 const gchar *digest_string;
767 g_checksum_update (md5_context, (const guchar *) method, strlen (method));
768 g_checksum_update (md5_context, (const guchar *) ":", 1);
769 g_checksum_update (md5_context, (const guchar *) uri, strlen (uri));
770 digest_string = g_checksum_get_string (md5_context);
771 memcpy (hex_a2, digest_string, strlen (digest_string));
774 g_checksum_reset (md5_context);
775 g_checksum_update (md5_context, (const guchar *) hex_a1, strlen (hex_a1));
776 g_checksum_update (md5_context, (const guchar *) ":", 1);
777 g_checksum_update (md5_context, (const guchar *) nonce, strlen (nonce));
778 g_checksum_update (md5_context, (const guchar *) ":", 1);
780 g_checksum_update (md5_context, (const guchar *) hex_a2, 32);
781 digest_string = g_checksum_get_string (md5_context);
782 memset (response, 0, 33);
783 memcpy (response, digest_string, strlen (digest_string));
785 g_checksum_free (md5_context);
789 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message)
791 switch (conn->auth_method) {
792 case GST_RTSP_AUTH_BASIC:{
797 if (conn->username == NULL || conn->passwd == NULL)
800 user_pass = g_strdup_printf ("%s:%s", conn->username, conn->passwd);
801 user_pass64 = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
802 auth_string = g_strdup_printf ("Basic %s", user_pass64);
804 gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
808 g_free (user_pass64);
811 case GST_RTSP_AUTH_DIGEST:{
812 gchar response[33], hex_urp[33];
813 gchar *auth_string, *auth_string2;
820 /* we need to have some params set */
821 if (conn->auth_params == NULL || conn->username == NULL ||
822 conn->passwd == NULL)
825 /* we need the realm and nonce */
826 realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
827 nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
828 if (realm == NULL || nonce == NULL)
831 auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
834 method = gst_rtsp_method_as_text (message->type_data.request.method);
835 uri = message->type_data.request.uri;
837 /* Assume no qop, algorithm=md5, stale=false */
838 /* For algorithm MD5, a1 = urp. */
839 auth_digest_compute_response (method, uri, hex_urp, nonce, response);
840 auth_string = g_strdup_printf ("Digest username=\"%s\", "
841 "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
842 conn->username, realm, nonce, uri, response);
844 opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
846 auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
848 g_free (auth_string);
849 auth_string = auth_string2;
851 gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
862 gen_date_string (gchar * date_string, guint len)
864 static const char wkdays[7][4] =
865 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
866 static const char months[12][4] =
867 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
881 g_snprintf (date_string, len, "%s, %02d %s %04d %02d:%02d:%02d GMT",
882 wkdays[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], tm.tm_year + 1900,
883 tm.tm_hour, tm.tm_min, tm.tm_sec);
887 write_bytes (GSocket * socket, const guint8 * buffer, guint * idx, guint size,
888 GCancellable * cancellable)
892 if (G_UNLIKELY (*idx > size))
893 return GST_RTSP_ERROR;
901 r = g_socket_send (socket, (gchar *) & buffer[*idx], left, cancellable,
903 if (G_UNLIKELY (r == 0)) {
904 return GST_RTSP_EINTR;
905 } else if (G_UNLIKELY (r < 0)) {
906 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
907 g_clear_error (&err);
908 return GST_RTSP_EINTR;
910 g_clear_error (&err);
911 return GST_RTSP_ESYS;
921 fill_raw_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size,
926 if (G_UNLIKELY (conn->initial_buffer != NULL)) {
927 gsize left = strlen (&conn->initial_buffer[conn->initial_buffer_offset]);
929 out = MIN (left, size);
930 memcpy (buffer, &conn->initial_buffer[conn->initial_buffer_offset], out);
932 if (left == (gsize) out) {
933 g_free (conn->initial_buffer);
934 conn->initial_buffer = NULL;
935 conn->initial_buffer_offset = 0;
937 conn->initial_buffer_offset += out;
940 if (G_LIKELY (size > (guint) out)) {
943 r = g_socket_receive (conn->read_socket, (gchar *) & buffer[out],
944 size - out, conn->cancellable, err);
956 fill_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size,
959 DecodeCtx *ctx = conn->ctxp;
964 guint8 in[sizeof (ctx->out) * 4 / 3];
967 while (size > 0 && ctx->cout < ctx->coutl) {
968 /* we have some leftover bytes */
969 *buffer++ = ctx->out[ctx->cout++];
974 /* got what we needed? */
978 /* try to read more bytes */
979 r = fill_raw_bytes (conn, in, sizeof (in), err);
988 g_base64_decode_step ((gchar *) in, r, ctx->out, &ctx->state,
992 out = fill_raw_bytes (conn, buffer, size, err);
999 read_bytes (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1004 if (G_UNLIKELY (*idx > size))
1005 return GST_RTSP_ERROR;
1012 r = fill_bytes (conn, &buffer[*idx], left, &err);
1013 if (G_UNLIKELY (r == 0)) {
1014 return GST_RTSP_EEOF;
1015 } else if (G_UNLIKELY (r < 0)) {
1016 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1017 g_clear_error (&err);
1018 return GST_RTSP_EINTR;
1020 g_clear_error (&err);
1021 return GST_RTSP_ESYS;
1030 /* The code below tries to handle clients using \r, \n or \r\n to indicate the
1031 * end of a line. It even does its best to handle clients which mix them (even
1032 * though this is a really stupid idea (tm).) It also handles Line White Space
1033 * (LWS), where a line end followed by whitespace is considered LWS. This is
1034 * the method used in RTSP (and HTTP) to break long lines.
1036 static GstRTSPResult
1037 read_line (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1045 if (conn->read_ahead == READ_AHEAD_EOH) {
1046 /* the last call to read_line() already determined that we have reached
1047 * the end of the headers, so convey that information now */
1048 conn->read_ahead = 0;
1050 } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1051 /* the last call to read_line() left off after having read \r\n */
1053 } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1054 /* the last call to read_line() left off after having read \r\n\r */
1056 } else if (conn->read_ahead != 0) {
1057 /* the last call to read_line() left us with a character to start with */
1058 c = (guint8) conn->read_ahead;
1059 conn->read_ahead = 0;
1061 /* read the next character */
1062 r = fill_bytes (conn, &c, 1, &err);
1063 if (G_UNLIKELY (r == 0)) {
1064 return GST_RTSP_EEOF;
1065 } else if (G_UNLIKELY (r < 0)) {
1066 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1067 g_clear_error (&err);
1068 return GST_RTSP_EINTR;
1071 g_clear_error (&err);
1072 return GST_RTSP_ESYS;
1076 /* special treatment of line endings */
1077 if (c == '\r' || c == '\n') {
1081 /* need to read ahead one more character to know what to do... */
1082 r = fill_bytes (conn, &read_ahead, 1, &err);
1083 if (G_UNLIKELY (r == 0)) {
1084 return GST_RTSP_EEOF;
1085 } else if (G_UNLIKELY (r < 0)) {
1086 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1087 /* remember the original character we read and try again next time */
1088 if (conn->read_ahead == 0)
1089 conn->read_ahead = c;
1090 return GST_RTSP_EINTR;
1091 g_clear_error (&err);
1092 return GST_RTSP_EINTR;
1095 g_clear_error (&err);
1096 return GST_RTSP_ESYS;
1099 if (read_ahead == ' ' || read_ahead == '\t') {
1100 if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1101 /* got \r\n\r followed by whitespace, treat it as a normal line
1102 * followed by one starting with LWS */
1103 conn->read_ahead = read_ahead;
1106 /* got LWS, change the line ending to a space and continue */
1108 conn->read_ahead = read_ahead;
1110 } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1111 if (read_ahead == '\r' || read_ahead == '\n') {
1112 /* got \r\n\r\r or \r\n\r\n, treat it as the end of the headers */
1113 conn->read_ahead = READ_AHEAD_EOH;
1116 /* got \r\n\r followed by something else, this is not really
1117 * supported since we have probably just eaten the first character
1118 * of the body or the next message, so just ignore the second \r
1119 * and live with it... */
1120 conn->read_ahead = read_ahead;
1123 } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1124 if (read_ahead == '\r') {
1125 /* got \r\n\r so far, need one more character... */
1126 conn->read_ahead = READ_AHEAD_CRLFCR;
1128 } else if (read_ahead == '\n') {
1129 /* got \r\n\n, treat it as the end of the headers */
1130 conn->read_ahead = READ_AHEAD_EOH;
1133 /* found the end of a line, keep read_ahead for the next line */
1134 conn->read_ahead = read_ahead;
1137 } else if (c == read_ahead) {
1138 /* got double \r or \n, treat it as the end of the headers */
1139 conn->read_ahead = READ_AHEAD_EOH;
1141 } else if (c == '\r' && read_ahead == '\n') {
1142 /* got \r\n so far, still need more to know what to do... */
1143 conn->read_ahead = READ_AHEAD_CRLF;
1146 /* found the end of a line, keep read_ahead for the next line */
1147 conn->read_ahead = read_ahead;
1152 if (G_LIKELY (*idx < size - 1))
1153 buffer[(*idx)++] = c;
1155 buffer[*idx] = '\0';
1161 * gst_rtsp_connection_write:
1162 * @conn: a #GstRTSPConnection
1163 * @data: the data to write
1164 * @size: the size of @data
1165 * @timeout: a timeout value or #NULL
1167 * Attempt to write @size bytes of @data to the connected @conn, blocking up to
1168 * the specified @timeout. @timeout can be #NULL, in which case this function
1169 * might block forever.
1171 * This function can be cancelled with gst_rtsp_connection_flush().
1173 * Returns: #GST_RTSP_OK on success.
1176 gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
1177 guint size, GTimeVal * timeout)
1184 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1185 g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
1186 g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
1188 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1195 write_bytes (conn->write_socket, data, &offset, size,
1197 if (G_LIKELY (res == GST_RTSP_OK))
1199 if (G_UNLIKELY (res != GST_RTSP_EINTR))
1202 /* not all is written, wait until we can write more */
1203 g_socket_set_timeout (conn->write_socket,
1204 (to + GST_SECOND - 1) / GST_SECOND);
1205 if (!g_socket_condition_wait (conn->write_socket,
1206 G_IO_OUT | G_IO_ERR | G_IO_HUP, conn->cancellable, &err)) {
1207 g_socket_set_timeout (conn->write_socket, 0);
1208 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
1209 g_clear_error (&err);
1211 } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
1212 g_clear_error (&err);
1215 g_clear_error (&err);
1218 g_socket_set_timeout (conn->write_socket, 0);
1225 return GST_RTSP_ETIMEOUT;
1229 return GST_RTSP_ESYS;
1233 return GST_RTSP_EINTR;
1242 message_to_string (GstRTSPConnection * conn, GstRTSPMessage * message)
1244 GString *str = NULL;
1246 str = g_string_new ("");
1248 switch (message->type) {
1249 case GST_RTSP_MESSAGE_REQUEST:
1250 /* create request string, add CSeq */
1251 g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
1253 gst_rtsp_method_as_text (message->type_data.request.method),
1254 message->type_data.request.uri, conn->cseq++);
1255 /* add session id if we have one */
1256 if (conn->session_id[0] != '\0') {
1257 gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
1258 gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
1261 /* add any authentication headers */
1262 add_auth_header (conn, message);
1264 case GST_RTSP_MESSAGE_RESPONSE:
1265 /* create response string */
1266 g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
1267 message->type_data.response.code, message->type_data.response.reason);
1269 case GST_RTSP_MESSAGE_HTTP_REQUEST:
1270 /* create request string */
1271 g_string_append_printf (str, "%s %s HTTP/%s\r\n",
1272 gst_rtsp_method_as_text (message->type_data.request.method),
1273 message->type_data.request.uri,
1274 gst_rtsp_version_as_text (message->type_data.request.version));
1275 /* add any authentication headers */
1276 add_auth_header (conn, message);
1278 case GST_RTSP_MESSAGE_HTTP_RESPONSE:
1279 /* create response string */
1280 g_string_append_printf (str, "HTTP/%s %d %s\r\n",
1281 gst_rtsp_version_as_text (message->type_data.request.version),
1282 message->type_data.response.code, message->type_data.response.reason);
1284 case GST_RTSP_MESSAGE_DATA:
1286 guint8 data_header[4];
1288 /* prepare data header */
1289 data_header[0] = '$';
1290 data_header[1] = message->type_data.data.channel;
1291 data_header[2] = (message->body_size >> 8) & 0xff;
1292 data_header[3] = message->body_size & 0xff;
1294 /* create string with header and data */
1295 str = g_string_append_len (str, (gchar *) data_header, 4);
1297 g_string_append_len (str, (gchar *) message->body,
1298 message->body_size);
1302 g_string_free (str, TRUE);
1303 g_return_val_if_reached (NULL);
1307 /* append headers and body */
1308 if (message->type != GST_RTSP_MESSAGE_DATA) {
1309 gchar date_string[100];
1311 gen_date_string (date_string, sizeof (date_string));
1313 /* add date header */
1314 gst_rtsp_message_remove_header (message, GST_RTSP_HDR_DATE, -1);
1315 gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
1317 /* append headers */
1318 gst_rtsp_message_append_headers (message, str);
1320 /* append Content-Length and body if needed */
1321 if (message->body != NULL && message->body_size > 0) {
1324 len = g_strdup_printf ("%d", message->body_size);
1325 g_string_append_printf (str, "%s: %s\r\n",
1326 gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
1328 /* header ends here */
1329 g_string_append (str, "\r\n");
1331 g_string_append_len (str, (gchar *) message->body,
1332 message->body_size);
1334 /* just end headers */
1335 g_string_append (str, "\r\n");
1343 * gst_rtsp_connection_send:
1344 * @conn: a #GstRTSPConnection
1345 * @message: the message to send
1346 * @timeout: a timeout value or #NULL
1348 * Attempt to send @message to the connected @conn, blocking up to
1349 * the specified @timeout. @timeout can be #NULL, in which case this function
1350 * might block forever.
1352 * This function can be cancelled with gst_rtsp_connection_flush().
1354 * Returns: #GST_RTSP_OK on success.
1357 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
1360 GString *string = NULL;
1365 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1366 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1368 if (G_UNLIKELY (!(string = message_to_string (conn, message))))
1371 if (conn->tunneled) {
1372 str = g_base64_encode ((const guchar *) string->str, string->len);
1373 g_string_free (string, TRUE);
1378 g_string_free (string, FALSE);
1382 res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
1390 g_warning ("Wrong message");
1391 return GST_RTSP_EINVAL;
1395 static GstRTSPResult
1396 parse_string (gchar * dest, gint size, gchar ** src)
1398 GstRTSPResult res = GST_RTSP_OK;
1403 while (g_ascii_isspace (**src))
1406 while (!g_ascii_isspace (**src) && **src != '\0') {
1408 dest[idx++] = **src;
1410 res = GST_RTSP_EPARSE;
1419 static GstRTSPResult
1420 parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
1421 GstRTSPVersion * version)
1423 GstRTSPResult res = GST_RTSP_OK;
1426 if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
1433 /* the version number must be formatted as X.Y with nothing following */
1434 if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
1435 res = GST_RTSP_EPARSE;
1437 if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
1438 if (major != 1 || minor != 0) {
1439 *version = GST_RTSP_VERSION_INVALID;
1440 res = GST_RTSP_ERROR;
1442 } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
1443 if (*type == GST_RTSP_MESSAGE_REQUEST)
1444 *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
1445 else if (*type == GST_RTSP_MESSAGE_RESPONSE)
1446 *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
1448 if (major == 1 && minor == 1) {
1449 *version = GST_RTSP_VERSION_1_1;
1450 } else if (major != 1 || minor != 0) {
1451 *version = GST_RTSP_VERSION_INVALID;
1452 res = GST_RTSP_ERROR;
1455 res = GST_RTSP_EPARSE;
1457 res = GST_RTSP_EPARSE;
1462 static GstRTSPResult
1463 parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
1465 GstRTSPResult res = GST_RTSP_OK;
1467 gchar versionstr[20];
1472 bptr = (gchar *) buffer;
1474 if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1475 res = GST_RTSP_EPARSE;
1477 if (parse_string (codestr, sizeof (codestr), &bptr) != GST_RTSP_OK)
1478 res = GST_RTSP_EPARSE;
1479 code = atoi (codestr);
1480 if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
1481 res = GST_RTSP_EPARSE;
1483 while (g_ascii_isspace (*bptr))
1486 if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
1487 NULL) != GST_RTSP_OK))
1488 res = GST_RTSP_EPARSE;
1490 res2 = parse_protocol_version (versionstr, &msg->type,
1491 &msg->type_data.response.version);
1492 if (G_LIKELY (res == GST_RTSP_OK))
1498 static GstRTSPResult
1499 parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
1501 GstRTSPResult res = GST_RTSP_OK;
1503 gchar versionstr[20];
1504 gchar methodstr[20];
1507 GstRTSPMethod method;
1509 bptr = (gchar *) buffer;
1511 if (parse_string (methodstr, sizeof (methodstr), &bptr) != GST_RTSP_OK)
1512 res = GST_RTSP_EPARSE;
1513 method = gst_rtsp_find_method (methodstr);
1515 if (parse_string (urlstr, sizeof (urlstr), &bptr) != GST_RTSP_OK)
1516 res = GST_RTSP_EPARSE;
1517 if (G_UNLIKELY (*urlstr == '\0'))
1518 res = GST_RTSP_EPARSE;
1520 if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1521 res = GST_RTSP_EPARSE;
1523 if (G_UNLIKELY (*bptr != '\0'))
1524 res = GST_RTSP_EPARSE;
1526 if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
1527 urlstr) != GST_RTSP_OK))
1528 res = GST_RTSP_EPARSE;
1530 res2 = parse_protocol_version (versionstr, &msg->type,
1531 &msg->type_data.request.version);
1532 if (G_LIKELY (res == GST_RTSP_OK))
1535 if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
1536 /* GET and POST are not allowed as RTSP methods */
1537 if (msg->type_data.request.method == GST_RTSP_GET ||
1538 msg->type_data.request.method == GST_RTSP_POST) {
1539 msg->type_data.request.method = GST_RTSP_INVALID;
1540 if (res == GST_RTSP_OK)
1541 res = GST_RTSP_ERROR;
1543 } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
1544 /* only GET and POST are allowed as HTTP methods */
1545 if (msg->type_data.request.method != GST_RTSP_GET &&
1546 msg->type_data.request.method != GST_RTSP_POST) {
1547 msg->type_data.request.method = GST_RTSP_INVALID;
1548 if (res == GST_RTSP_OK)
1549 res = GST_RTSP_ERROR;
1556 /* parsing lines means reading a Key: Value pair */
1557 static GstRTSPResult
1558 parse_line (guint8 * buffer, GstRTSPMessage * msg)
1560 GstRTSPHeaderField field;
1561 gchar *line = (gchar *) buffer;
1564 if ((value = strchr (line, ':')) == NULL || value == line)
1567 /* trim space before the colon */
1568 if (value[-1] == ' ')
1571 /* replace the colon with a NUL */
1574 /* find the header */
1575 field = gst_rtsp_find_header_field (line);
1576 if (field == GST_RTSP_HDR_INVALID)
1579 /* split up the value in multiple key:value pairs if it contains comma(s) */
1580 while (*value != '\0') {
1582 gchar *comma = NULL;
1583 gboolean quoted = FALSE;
1586 /* trim leading space */
1590 /* for headers which may not appear multiple times, and thus may not
1591 * contain multiple values on the same line, we can short-circuit the loop
1592 * below and the entire value results in just one key:value pair*/
1593 if (!gst_rtsp_header_allow_multiple (field))
1594 next_value = value + strlen (value);
1598 /* find the next value, taking special care of quotes and comments */
1599 while (*next_value != '\0') {
1600 if ((quoted || comment != 0) && *next_value == '\\' &&
1601 next_value[1] != '\0')
1603 else if (comment == 0 && *next_value == '"')
1605 else if (!quoted && *next_value == '(')
1607 else if (comment != 0 && *next_value == ')')
1609 else if (!quoted && comment == 0) {
1610 /* To quote RFC 2068: "User agents MUST take special care in parsing
1611 * the WWW-Authenticate field value if it contains more than one
1612 * challenge, or if more than one WWW-Authenticate header field is
1613 * provided, since the contents of a challenge may itself contain a
1614 * comma-separated list of authentication parameters."
1616 * What this means is that we cannot just look for an unquoted comma
1617 * when looking for multiple values in Proxy-Authenticate and
1618 * WWW-Authenticate headers. Instead we need to look for the sequence
1619 * "comma [space] token space token" before we can split after the
1622 if (field == GST_RTSP_HDR_PROXY_AUTHENTICATE ||
1623 field == GST_RTSP_HDR_WWW_AUTHENTICATE) {
1624 if (*next_value == ',') {
1625 if (next_value[1] == ' ') {
1626 /* skip any space following the comma so we do not mistake it for
1627 * separating between two tokens */
1631 } else if (*next_value == ' ' && next_value[1] != ',' &&
1632 next_value[1] != '=' && comma != NULL) {
1637 } else if (*next_value == ',')
1645 if (value != next_value && next_value[-1] == ' ')
1646 next_value[-1] = '\0';
1648 if (*next_value != '\0')
1649 *next_value++ = '\0';
1651 /* add the key:value pair */
1653 gst_rtsp_message_add_header (msg, field, value);
1664 return GST_RTSP_EPARSE;
1668 /* convert all consecutive whitespace to a single space */
1670 normalize_line (guint8 * buffer)
1673 if (g_ascii_isspace (*buffer)) {
1677 for (tmp = buffer; g_ascii_isspace (*tmp); tmp++) {
1680 memmove (buffer, tmp, strlen ((gchar *) tmp) + 1);
1688 * GST_RTSP_OK when a complete message was read.
1689 * GST_RTSP_EEOF: when the read socket is closed
1690 * GST_RTSP_EINTR: when more data is needed.
1691 * GST_RTSP_..: some other error occured.
1693 static GstRTSPResult
1694 build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
1695 GstRTSPConnection * conn)
1700 switch (builder->state) {
1705 builder->offset = 0;
1707 read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 1);
1708 if (res != GST_RTSP_OK)
1711 c = builder->buffer[0];
1713 /* we have 1 bytes now and we can see if this is a data message or
1716 /* data message, prepare for the header */
1717 builder->state = STATE_DATA_HEADER;
1718 } else if (c == '\n' || c == '\r') {
1719 /* skip \n and \r */
1720 builder->offset = 0;
1723 builder->state = STATE_READ_LINES;
1727 case STATE_DATA_HEADER:
1730 read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 4);
1731 if (res != GST_RTSP_OK)
1734 gst_rtsp_message_init_data (message, builder->buffer[1]);
1736 builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3];
1737 builder->body_data = g_malloc (builder->body_len + 1);
1738 builder->body_data[builder->body_len] = '\0';
1739 builder->offset = 0;
1740 builder->state = STATE_DATA_BODY;
1743 case STATE_DATA_BODY:
1746 read_bytes (conn, builder->body_data, &builder->offset,
1748 if (res != GST_RTSP_OK)
1751 /* we have the complete body now, store in the message adjusting the
1752 * length to include the trailing '\0' */
1753 gst_rtsp_message_take_body (message,
1754 (guint8 *) builder->body_data, builder->body_len + 1);
1755 builder->body_data = NULL;
1756 builder->body_len = 0;
1758 builder->state = STATE_END;
1761 case STATE_READ_LINES:
1763 res = read_line (conn, builder->buffer, &builder->offset,
1764 sizeof (builder->buffer));
1765 if (res != GST_RTSP_OK)
1768 /* we have a regular response */
1769 if (builder->buffer[0] == '\0') {
1772 /* empty line, end of message header */
1773 /* see if there is a Content-Length header, but ignore it if this
1774 * is a POST request with an x-sessioncookie header */
1775 if (gst_rtsp_message_get_header (message,
1776 GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK &&
1777 (message->type != GST_RTSP_MESSAGE_HTTP_REQUEST ||
1778 message->type_data.request.method != GST_RTSP_POST ||
1779 gst_rtsp_message_get_header (message,
1780 GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
1781 /* there is, prepare to read the body */
1782 builder->body_len = atol (hdrval);
1783 builder->body_data = g_try_malloc (builder->body_len + 1);
1784 /* we can't do much here, we need the length to know how many bytes
1785 * we need to read next and when allocation fails, something is
1786 * probably wrong with the length. */
1787 if (builder->body_data == NULL)
1788 goto invalid_body_len;
1790 builder->body_data[builder->body_len] = '\0';
1791 builder->offset = 0;
1792 builder->state = STATE_DATA_BODY;
1794 builder->state = STATE_END;
1799 /* we have a line */
1800 normalize_line (builder->buffer);
1801 if (builder->line == 0) {
1802 /* first line, check for response status */
1803 if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
1804 memcmp (builder->buffer, "HTTP", 4) == 0) {
1805 builder->status = parse_response_status (builder->buffer, message);
1807 builder->status = parse_request_line (builder->buffer, message);
1810 /* else just parse the line */
1811 res = parse_line (builder->buffer, message);
1812 if (res != GST_RTSP_OK)
1813 builder->status = res;
1816 builder->offset = 0;
1821 gchar *session_cookie;
1824 if (message->type == GST_RTSP_MESSAGE_DATA) {
1825 /* data messages don't have headers */
1830 /* save the tunnel session in the connection */
1831 if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
1832 !conn->manual_http &&
1833 conn->tstate == TUNNEL_STATE_NONE &&
1834 gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
1835 &session_cookie, 0) == GST_RTSP_OK) {
1836 strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
1837 conn->tunnelid[TUNNELID_LEN - 1] = '\0';
1838 conn->tunneled = TRUE;
1841 /* save session id in the connection for further use */
1842 if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
1843 gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
1844 &session_id, 0) == GST_RTSP_OK) {
1847 maxlen = sizeof (conn->session_id) - 1;
1848 /* the sessionid can have attributes marked with ;
1849 * Make sure we strip them */
1850 for (i = 0; session_id[i] != '\0'; i++) {
1851 if (session_id[i] == ';') {
1856 } while (g_ascii_isspace (session_id[i]));
1857 if (g_str_has_prefix (&session_id[i], "timeout=")) {
1860 /* if we parsed something valid, configure */
1861 if ((to = atoi (&session_id[i + 8])) > 0)
1868 /* make sure to not overflow */
1869 strncpy (conn->session_id, session_id, maxlen);
1870 conn->session_id[maxlen] = '\0';
1872 res = builder->status;
1876 res = GST_RTSP_ERROR;
1886 GST_DEBUG ("could not allocate body");
1887 return GST_RTSP_ERROR;
1892 * gst_rtsp_connection_read:
1893 * @conn: a #GstRTSPConnection
1894 * @data: the data to read
1895 * @size: the size of @data
1896 * @timeout: a timeout value or #NULL
1898 * Attempt to read @size bytes into @data from the connected @conn, blocking up to
1899 * the specified @timeout. @timeout can be #NULL, in which case this function
1900 * might block forever.
1902 * This function can be cancelled with gst_rtsp_connection_flush().
1904 * Returns: #GST_RTSP_OK on success.
1907 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
1915 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1916 g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
1917 g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
1919 if (G_UNLIKELY (size == 0))
1924 /* configure timeout if any */
1925 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1928 res = read_bytes (conn, data, &offset, size);
1929 if (G_UNLIKELY (res == GST_RTSP_EEOF))
1931 if (G_LIKELY (res == GST_RTSP_OK))
1933 if (G_UNLIKELY (res != GST_RTSP_EINTR))
1936 g_socket_set_timeout (conn->read_socket,
1937 (to + GST_SECOND - 1) / GST_SECOND);
1938 if (!g_socket_condition_wait (conn->read_socket,
1939 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable,
1941 g_socket_set_timeout (conn->read_socket, 0);
1942 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
1943 g_clear_error (&err);
1945 } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
1946 g_clear_error (&err);
1947 goto select_timeout;
1949 g_clear_error (&err);
1952 g_socket_set_timeout (conn->read_socket, 0);
1959 return GST_RTSP_ESYS;
1963 return GST_RTSP_ETIMEOUT;
1967 return GST_RTSP_EINTR;
1971 return GST_RTSP_EEOF;
1979 static GstRTSPMessage *
1980 gen_tunnel_reply (GstRTSPConnection * conn, GstRTSPStatusCode code,
1981 const GstRTSPMessage * request)
1983 GstRTSPMessage *msg;
1986 if (gst_rtsp_status_as_text (code) == NULL)
1987 code = GST_RTSP_STS_INTERNAL_SERVER_ERROR;
1989 GST_RTSP_CHECK (gst_rtsp_message_new_response (&msg, code, NULL, request),
1992 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SERVER,
1993 "GStreamer RTSP Server");
1994 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONNECTION, "close");
1995 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-store");
1996 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
1998 if (code == GST_RTSP_STS_OK) {
2000 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
2002 gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_TYPE,
2003 "application/x-rtsp-tunnelled");
2016 * gst_rtsp_connection_receive:
2017 * @conn: a #GstRTSPConnection
2018 * @message: the message to read
2019 * @timeout: a timeout value or #NULL
2021 * Attempt to read into @message from the connected @conn, blocking up to
2022 * the specified @timeout. @timeout can be #NULL, in which case this function
2023 * might block forever.
2025 * This function can be cancelled with gst_rtsp_connection_flush().
2027 * Returns: #GST_RTSP_OK on success.
2030 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
2034 GstRTSPBuilder builder;
2038 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2039 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2040 g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2042 /* configure timeout if any */
2043 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2045 memset (&builder, 0, sizeof (GstRTSPBuilder));
2047 res = build_next (&builder, message, conn);
2048 if (G_UNLIKELY (res == GST_RTSP_EEOF))
2050 else if (G_LIKELY (res == GST_RTSP_OK)) {
2051 if (!conn->manual_http) {
2052 if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2053 if (conn->tstate == TUNNEL_STATE_NONE &&
2054 message->type_data.request.method == GST_RTSP_GET) {
2055 GstRTSPMessage *response;
2057 conn->tstate = TUNNEL_STATE_GET;
2059 /* tunnel GET request, we can reply now */
2060 response = gen_tunnel_reply (conn, GST_RTSP_STS_OK, message);
2061 res = gst_rtsp_connection_send (conn, response, timeout);
2062 gst_rtsp_message_free (response);
2063 if (res == GST_RTSP_OK)
2064 res = GST_RTSP_ETGET;
2066 } else if (conn->tstate == TUNNEL_STATE_NONE &&
2067 message->type_data.request.method == GST_RTSP_POST) {
2068 conn->tstate = TUNNEL_STATE_POST;
2070 /* tunnel POST request, the caller now has to link the two
2072 res = GST_RTSP_ETPOST;
2075 res = GST_RTSP_EPARSE;
2078 } else if (message->type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
2079 res = GST_RTSP_EPARSE;
2085 } else if (G_UNLIKELY (res != GST_RTSP_EINTR))
2088 g_socket_set_timeout (conn->read_socket,
2089 (to + GST_SECOND - 1) / GST_SECOND);
2090 if (!g_socket_condition_wait (conn->read_socket,
2091 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable,
2093 g_socket_set_timeout (conn->read_socket, 0);
2094 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
2095 g_clear_error (&err);
2097 } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
2098 g_clear_error (&err);
2099 goto select_timeout;
2101 g_clear_error (&err);
2104 g_socket_set_timeout (conn->read_socket, 0);
2107 /* we have a message here */
2108 build_reset (&builder);
2115 res = GST_RTSP_ESYS;
2120 res = GST_RTSP_ETIMEOUT;
2125 res = GST_RTSP_EINTR;
2130 res = GST_RTSP_EEOF;
2136 build_reset (&builder);
2137 gst_rtsp_message_unset (message);
2143 * gst_rtsp_connection_close:
2144 * @conn: a #GstRTSPConnection
2146 * Close the connected @conn. After this call, the connection is in the same
2147 * state as when it was first created.
2149 * Returns: #GST_RTSP_OK on success.
2152 gst_rtsp_connection_close (GstRTSPConnection * conn)
2154 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2159 conn->read_ahead = 0;
2161 g_free (conn->initial_buffer);
2162 conn->initial_buffer = NULL;
2163 conn->initial_buffer_offset = 0;
2165 conn->write_socket = NULL;
2166 conn->read_socket = NULL;
2167 conn->tunneled = FALSE;
2168 conn->tstate = TUNNEL_STATE_NONE;
2170 g_free (conn->username);
2171 conn->username = NULL;
2172 g_free (conn->passwd);
2173 conn->passwd = NULL;
2174 gst_rtsp_connection_clear_auth_params (conn);
2177 conn->session_id[0] = '\0';
2183 * gst_rtsp_connection_free:
2184 * @conn: a #GstRTSPConnection
2186 * Close and free @conn.
2188 * Returns: #GST_RTSP_OK on success.
2191 gst_rtsp_connection_free (GstRTSPConnection * conn)
2195 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2197 res = gst_rtsp_connection_close (conn);
2199 g_object_unref (conn->socket0);
2201 g_object_unref (conn->socket1);
2203 if (conn->cancellable)
2204 g_object_unref (conn->cancellable);
2206 g_timer_destroy (conn->timer);
2207 gst_rtsp_url_free (conn->url);
2208 g_free (conn->proxy_host);
2215 * gst_rtsp_connection_poll:
2216 * @conn: a #GstRTSPConnection
2217 * @events: a bitmask of #GstRTSPEvent flags to check
2218 * @revents: location for result flags
2219 * @timeout: a timeout
2221 * Wait up to the specified @timeout for the connection to become available for
2222 * at least one of the operations specified in @events. When the function returns
2223 * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
2226 * @timeout can be #NULL, in which case this function might block forever.
2228 * This function can be cancelled with gst_rtsp_connection_flush().
2230 * Returns: #GST_RTSP_OK on success.
2235 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
2236 GstRTSPEvent * revents, GTimeVal * timeout)
2240 GSource *rs, *ws, *ts;
2241 GIOCondition condition;
2243 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2244 g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
2245 g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
2246 g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2247 g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
2249 ctx = g_main_context_new ();
2251 /* configure timeout if any */
2252 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2255 ts = g_timeout_source_new (to / GST_MSECOND);
2256 g_source_set_dummy_callback (ts);
2257 g_source_attach (ts, ctx);
2258 g_source_unref (ts);
2261 rs = g_socket_create_source (conn->read_socket,
2262 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable);
2263 g_source_set_dummy_callback (rs);
2264 g_source_attach (rs, ctx);
2265 g_source_unref (rs);
2267 ws = g_socket_create_source (conn->write_socket,
2268 G_IO_OUT | G_IO_ERR | G_IO_HUP, conn->cancellable);
2269 g_source_set_dummy_callback (ws);
2270 g_source_attach (ws, ctx);
2271 g_source_unref (ws);
2273 /* Returns after handling all pending events */
2274 g_main_context_iteration (ctx, TRUE);
2276 g_main_context_unref (ctx);
2279 g_socket_condition_check (conn->read_socket,
2280 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP);
2282 g_socket_condition_check (conn->write_socket,
2283 G_IO_OUT | G_IO_ERR | G_IO_HUP);
2286 if (events & GST_RTSP_EV_READ) {
2287 if ((condition & G_IO_IN) || (condition & G_IO_PRI))
2288 *revents |= GST_RTSP_EV_READ;
2290 if (events & GST_RTSP_EV_WRITE) {
2291 if ((condition & G_IO_OUT))
2292 *revents |= GST_RTSP_EV_WRITE;
2296 return GST_RTSP_ETIMEOUT;
2302 * gst_rtsp_connection_next_timeout:
2303 * @conn: a #GstRTSPConnection
2304 * @timeout: a timeout
2306 * Calculate the next timeout for @conn, storing the result in @timeout.
2308 * Returns: #GST_RTSP_OK.
2311 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
2318 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2319 g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
2321 ctimeout = conn->timeout;
2322 if (ctimeout >= 20) {
2323 /* Because we should act before the timeout we timeout 5
2324 * seconds in advance. */
2326 } else if (ctimeout >= 5) {
2327 /* else timeout 20% earlier */
2328 ctimeout -= ctimeout / 5;
2329 } else if (ctimeout >= 1) {
2330 /* else timeout 1 second earlier */
2334 elapsed = g_timer_elapsed (conn->timer, &usec);
2335 if (elapsed >= ctimeout) {
2339 sec = ctimeout - elapsed;
2340 if (usec <= G_USEC_PER_SEC)
2341 usec = G_USEC_PER_SEC - usec;
2346 timeout->tv_sec = sec;
2347 timeout->tv_usec = usec;
2353 * gst_rtsp_connection_reset_timeout:
2354 * @conn: a #GstRTSPConnection
2356 * Reset the timeout of @conn.
2358 * Returns: #GST_RTSP_OK.
2361 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
2363 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2365 g_timer_start (conn->timer);
2371 * gst_rtsp_connection_flush:
2372 * @conn: a #GstRTSPConnection
2373 * @flush: start or stop the flush
2375 * Start or stop the flushing action on @conn. When flushing, all current
2376 * and future actions on @conn will return #GST_RTSP_EINTR until the connection
2377 * is set to non-flushing mode again.
2379 * Returns: #GST_RTSP_OK.
2382 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
2384 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2387 g_cancellable_cancel (conn->cancellable);
2389 g_cancellable_reset (conn->cancellable);
2395 * gst_rtsp_connection_set_proxy:
2396 * @conn: a #GstRTSPConnection
2397 * @host: the proxy host
2398 * @port: the proxy port
2400 * Set the proxy host and port.
2402 * Returns: #GST_RTSP_OK.
2407 gst_rtsp_connection_set_proxy (GstRTSPConnection * conn,
2408 const gchar * host, guint port)
2410 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2412 g_free (conn->proxy_host);
2413 conn->proxy_host = g_strdup (host);
2414 conn->proxy_port = port;
2420 * gst_rtsp_connection_set_auth:
2421 * @conn: a #GstRTSPConnection
2422 * @method: authentication method
2424 * @pass: the password
2426 * Configure @conn for authentication mode @method with @user and @pass as the
2427 * user and password respectively.
2429 * Returns: #GST_RTSP_OK.
2432 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
2433 GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
2435 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2437 if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
2438 || g_strrstr (user, ":") != NULL))
2439 return GST_RTSP_EINVAL;
2441 /* Make sure the username and passwd are being set for authentication */
2442 if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
2443 return GST_RTSP_EINVAL;
2445 /* ":" chars are not allowed in usernames for basic auth */
2446 if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
2447 return GST_RTSP_EINVAL;
2449 g_free (conn->username);
2450 g_free (conn->passwd);
2452 conn->auth_method = method;
2453 conn->username = g_strdup (user);
2454 conn->passwd = g_strdup (pass);
2461 * @key: ASCII string to hash
2463 * Hashes @key in a case-insensitive manner.
2465 * Returns: the hash code.
2468 str_case_hash (gconstpointer key)
2470 const char *p = key;
2471 guint h = g_ascii_toupper (*p);
2474 for (p += 1; *p != '\0'; p++)
2475 h = (h << 5) - h + g_ascii_toupper (*p);
2482 * @v1: an ASCII string
2483 * @v2: another ASCII string
2485 * Compares @v1 and @v2 in a case-insensitive manner
2487 * Returns: %TRUE if they are equal (modulo case)
2490 str_case_equal (gconstpointer v1, gconstpointer v2)
2492 const char *string1 = v1;
2493 const char *string2 = v2;
2495 return g_ascii_strcasecmp (string1, string2) == 0;
2499 * gst_rtsp_connection_set_auth_param:
2500 * @conn: a #GstRTSPConnection
2501 * @param: authentication directive
2504 * Setup @conn with authentication directives. This is not necesary for
2505 * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
2506 * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
2507 * in the WWW-Authenticate response header and can include realm, domain,
2508 * nonce, opaque, stale, algorithm, qop as per RFC2617.
2513 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
2514 const gchar * param, const gchar * value)
2516 g_return_if_fail (conn != NULL);
2517 g_return_if_fail (param != NULL);
2519 if (conn->auth_params == NULL) {
2521 g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
2523 g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
2527 * gst_rtsp_connection_clear_auth_params:
2528 * @conn: a #GstRTSPConnection
2530 * Clear the list of authentication directives stored in @conn.
2535 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
2537 g_return_if_fail (conn != NULL);
2539 if (conn->auth_params != NULL) {
2540 g_hash_table_destroy (conn->auth_params);
2541 conn->auth_params = NULL;
2545 static GstRTSPResult
2546 set_qos_dscp (GSocket * socket, guint qos_dscp)
2552 union gst_sockaddr sa;
2553 socklen_t slen = sizeof (sa);
2560 fd = g_socket_get_fd (socket);
2561 if (getsockname (fd, &sa.sa, &slen) < 0)
2562 goto no_getsockname;
2564 af = sa.sa.sa_family;
2566 /* if this is an IPv4-mapped address then do IPv4 QoS */
2567 if (af == AF_INET6) {
2568 if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr))
2572 /* extract and shift 6 bits of the DSCP */
2573 tos = (qos_dscp & 0x3f) << 2;
2577 if (setsockopt (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
2582 if (setsockopt (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0)
2596 return GST_RTSP_ESYS;
2600 return GST_RTSP_ERROR;
2606 * gst_rtsp_connection_set_qos_dscp:
2607 * @conn: a #GstRTSPConnection
2608 * @qos_dscp: DSCP value
2610 * Configure @conn to use the specified DSCP value.
2612 * Returns: #GST_RTSP_OK on success.
2617 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
2621 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2622 g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2623 g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
2625 res = set_qos_dscp (conn->socket0, qos_dscp);
2626 if (res == GST_RTSP_OK)
2627 res = set_qos_dscp (conn->socket1, qos_dscp);
2634 * gst_rtsp_connection_get_url:
2635 * @conn: a #GstRTSPConnection
2637 * Retrieve the URL of the other end of @conn.
2639 * Returns: The URL. This value remains valid until the
2640 * connection is freed.
2645 gst_rtsp_connection_get_url (const GstRTSPConnection * conn)
2647 g_return_val_if_fail (conn != NULL, NULL);
2653 * gst_rtsp_connection_get_ip:
2654 * @conn: a #GstRTSPConnection
2656 * Retrieve the IP address of the other end of @conn.
2658 * Returns: The IP address as a string. this value remains valid until the
2659 * connection is closed.
2664 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
2666 g_return_val_if_fail (conn != NULL, NULL);
2672 * gst_rtsp_connection_set_ip:
2673 * @conn: a #GstRTSPConnection
2674 * @ip: an ip address
2676 * Set the IP address of the server.
2681 gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip)
2683 g_return_if_fail (conn != NULL);
2686 conn->ip = g_strdup (ip);
2690 * gst_rtsp_connection_get_readfd:
2691 * @conn: a #GstRTSPConnection
2693 * Get the file descriptor for reading.
2695 * Returns: the file descriptor used for reading or %NULL on error. The file
2696 * descriptor remains valid until the connection is closed.
2701 gst_rtsp_connection_get_read_socket (const GstRTSPConnection * conn)
2703 g_return_val_if_fail (conn != NULL, NULL);
2704 g_return_val_if_fail (conn->read_socket != NULL, NULL);
2706 return conn->read_socket;
2710 * gst_rtsp_connection_get_write_socket:
2711 * @conn: a #GstRTSPConnection
2713 * Get the file descriptor for writing.
2715 * Returns: the file descriptor used for writing or NULL on error. The file
2716 * descriptor remains valid until the connection is closed.
2721 gst_rtsp_connection_get_write_socket (const GstRTSPConnection * conn)
2723 g_return_val_if_fail (conn != NULL, NULL);
2724 g_return_val_if_fail (conn->write_socket != NULL, NULL);
2726 return conn->write_socket;
2730 * gst_rtsp_connection_set_http_mode:
2731 * @conn: a #GstRTSPConnection
2732 * @enable: %TRUE to enable manual HTTP mode
2734 * By setting the HTTP mode to %TRUE the message parsing will support HTTP
2735 * messages in addition to the RTSP messages. It will also disable the
2736 * automatic handling of setting up an HTTP tunnel.
2741 gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
2743 g_return_if_fail (conn != NULL);
2745 conn->manual_http = enable;
2749 * gst_rtsp_connection_set_tunneled:
2750 * @conn: a #GstRTSPConnection
2751 * @tunneled: the new state
2753 * Set the HTTP tunneling state of the connection. This must be configured before
2754 * the @conn is connected.
2759 gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
2761 g_return_if_fail (conn != NULL);
2762 g_return_if_fail (conn->read_socket == NULL);
2763 g_return_if_fail (conn->write_socket == NULL);
2765 conn->tunneled = tunneled;
2769 * gst_rtsp_connection_is_tunneled:
2770 * @conn: a #GstRTSPConnection
2772 * Get the tunneling state of the connection.
2774 * Returns: if @conn is using HTTP tunneling.
2779 gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn)
2781 g_return_val_if_fail (conn != NULL, FALSE);
2783 return conn->tunneled;
2787 * gst_rtsp_connection_get_tunnelid:
2788 * @conn: a #GstRTSPConnection
2790 * Get the tunnel session id the connection.
2792 * Returns: returns a non-empty string if @conn is being tunneled over HTTP.
2797 gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn)
2799 g_return_val_if_fail (conn != NULL, NULL);
2801 if (!conn->tunneled)
2804 return conn->tunnelid;
2808 * gst_rtsp_connection_do_tunnel:
2809 * @conn: a #GstRTSPConnection
2810 * @conn2: a #GstRTSPConnection or %NULL
2812 * If @conn received the first tunnel connection and @conn2 received
2813 * the second tunnel connection, link the two connections together so that
2814 * @conn manages the tunneled connection.
2816 * After this call, @conn2 cannot be used anymore and must be freed with
2817 * gst_rtsp_connection_free().
2819 * If @conn2 is %NULL then only the base64 decoding context will be setup for
2822 * Returns: return GST_RTSP_OK on success.
2827 gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn,
2828 GstRTSPConnection * conn2)
2830 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2832 if (conn2 != NULL) {
2833 g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL);
2834 g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL);
2835 g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid,
2836 TUNNELID_LEN), GST_RTSP_EINVAL);
2838 /* both connections have socket0 as the read/write socket. start by taking the
2839 * socket from conn2 and set it as the socket in conn */
2840 conn->socket1 = conn2->socket0;
2842 /* clean up some of the state of conn2 */
2843 g_cancellable_cancel (conn2->cancellable);
2845 g_object_unref (conn2->socket1);
2846 conn2->socket1 = NULL;
2847 conn2->write_socket = conn2->read_socket = NULL;
2848 g_cancellable_reset (conn2->cancellable);
2850 /* We make socket0 the write socket and socket1 the read socket. */
2851 conn->write_socket = conn->socket0;
2852 conn->read_socket = conn->socket1;
2854 conn->tstate = TUNNEL_STATE_COMPLETE;
2857 /* we need base64 decoding for the readfd */
2858 conn->ctx.state = 0;
2861 conn->ctx.coutl = 0;
2862 conn->ctxp = &conn->ctx;
2867 #define READ_ERR (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
2868 #define READ_COND (G_IO_IN | READ_ERR)
2869 #define WRITE_ERR (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
2870 #define WRITE_COND (G_IO_OUT | WRITE_ERR)
2879 /* async functions */
2880 struct _GstRTSPWatch
2884 GstRTSPConnection *conn;
2886 GstRTSPBuilder builder;
2887 GstRTSPMessage message;
2892 /* queued message for transmission */
2901 GstRTSPWatchFuncs funcs;
2904 GDestroyNotify notify;
2908 gst_rtsp_source_prepare (GSource * source, gint * timeout)
2910 GstRTSPWatch *watch = (GstRTSPWatch *) source;
2912 if (watch->conn->initial_buffer != NULL)
2915 *timeout = (watch->conn->timeout * 1000);
2921 gst_rtsp_source_check (GSource * source)
2923 GstRTSPWatch *watch = (GstRTSPWatch *) source;
2925 if (watch->readfd.revents & READ_COND)
2928 if (watch->writefd.revents & WRITE_COND)
2935 gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
2936 gpointer user_data G_GNUC_UNUSED)
2938 GstRTSPWatch *watch = (GstRTSPWatch *) source;
2939 GstRTSPResult res = GST_RTSP_ERROR;
2940 gboolean keep_running = TRUE;
2942 /* first read as much as we can */
2943 if (watch->readfd.revents & READ_COND || watch->conn->initial_buffer != NULL) {
2945 if (watch->readfd.revents & READ_ERR)
2948 res = build_next (&watch->builder, &watch->message, watch->conn);
2949 if (res == GST_RTSP_EINTR)
2951 else if (G_UNLIKELY (res == GST_RTSP_EEOF)) {
2952 watch->readfd.events = 0;
2953 watch->readfd.revents = 0;
2954 g_source_remove_poll ((GSource *) watch, &watch->readfd);
2955 /* When we are in tunnelled mode, the read socket can be closed and we
2956 * should be prepared for a new POST method to reopen it */
2957 if (watch->conn->tstate == TUNNEL_STATE_COMPLETE) {
2958 /* remove the read connection for the tunnel */
2959 /* we accept a new POST request */
2960 watch->conn->tstate = TUNNEL_STATE_GET;
2961 /* and signal that we lost our tunnel */
2962 if (watch->funcs.tunnel_lost)
2963 res = watch->funcs.tunnel_lost (watch, watch->user_data);
2967 } else if (G_LIKELY (res == GST_RTSP_OK)) {
2968 if (!watch->conn->manual_http &&
2969 watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2970 if (watch->conn->tstate == TUNNEL_STATE_NONE &&
2971 watch->message.type_data.request.method == GST_RTSP_GET) {
2972 GstRTSPMessage *response;
2973 GstRTSPStatusCode code;
2975 watch->conn->tstate = TUNNEL_STATE_GET;
2977 if (watch->funcs.tunnel_start)
2978 code = watch->funcs.tunnel_start (watch, watch->user_data);
2980 code = GST_RTSP_STS_OK;
2982 /* queue the response */
2983 response = gen_tunnel_reply (watch->conn, code, &watch->message);
2984 gst_rtsp_watch_send_message (watch, response, NULL);
2985 gst_rtsp_message_free (response);
2987 } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
2988 watch->message.type_data.request.method == GST_RTSP_POST) {
2989 watch->conn->tstate = TUNNEL_STATE_POST;
2991 /* in the callback the connection should be tunneled with the
2993 if (watch->funcs.tunnel_complete)
2994 watch->funcs.tunnel_complete (watch, watch->user_data);
3000 if (!watch->conn->manual_http) {
3001 /* if manual HTTP support is not enabled, then restore the message to
3002 * what it would have looked like without the support for parsing HTTP
3003 * messages being present */
3004 if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3005 watch->message.type = GST_RTSP_MESSAGE_REQUEST;
3006 watch->message.type_data.request.method = GST_RTSP_INVALID;
3007 if (watch->message.type_data.request.version != GST_RTSP_VERSION_1_0)
3008 watch->message.type_data.request.version = GST_RTSP_VERSION_INVALID;
3009 res = GST_RTSP_EPARSE;
3010 } else if (watch->message.type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
3011 watch->message.type = GST_RTSP_MESSAGE_RESPONSE;
3012 if (watch->message.type_data.response.version != GST_RTSP_VERSION_1_0)
3013 watch->message.type_data.response.version =
3014 GST_RTSP_VERSION_INVALID;
3015 res = GST_RTSP_EPARSE;
3019 if (G_LIKELY (res == GST_RTSP_OK)) {
3020 if (watch->funcs.message_received)
3021 watch->funcs.message_received (watch, &watch->message,
3028 gst_rtsp_message_unset (&watch->message);
3029 build_reset (&watch->builder);
3033 if (watch->writefd.revents & WRITE_COND) {
3034 if (watch->writefd.revents & WRITE_ERR)
3037 g_mutex_lock (watch->mutex);
3039 if (watch->write_data == NULL) {
3042 /* get a new message from the queue */
3043 rec = g_queue_pop_tail (watch->messages);
3047 watch->write_off = 0;
3048 watch->write_data = rec->data;
3049 watch->write_size = rec->size;
3050 watch->write_id = rec->id;
3052 g_slice_free (GstRTSPRec, rec);
3055 res = write_bytes (watch->conn->write_socket, watch->write_data,
3056 &watch->write_off, watch->write_size, watch->conn->cancellable);
3057 g_mutex_unlock (watch->mutex);
3059 if (res == GST_RTSP_EINTR)
3061 else if (G_LIKELY (res == GST_RTSP_OK)) {
3062 if (watch->funcs.message_sent)
3063 watch->funcs.message_sent (watch, watch->write_id, watch->user_data);
3067 g_mutex_lock (watch->mutex);
3069 g_free (watch->write_data);
3070 watch->write_data = NULL;
3073 watch->writefd.events = WRITE_ERR;
3075 g_mutex_unlock (watch->mutex);
3079 return keep_running;
3084 if (watch->funcs.closed)
3085 watch->funcs.closed (watch, watch->user_data);
3087 /* always stop when the readfd returns EOF in non-tunneled mode */
3092 watch->readfd.events = 0;
3093 watch->readfd.revents = 0;
3094 g_source_remove_poll ((GSource *) watch, &watch->readfd);
3095 keep_running = (watch->writefd.events != 0);
3098 if (watch->funcs.error_full)
3099 GST_RTSP_CHECK (watch->funcs.error_full (watch, res, &watch->message,
3100 0, watch->user_data), error);
3108 watch->writefd.events = 0;
3109 watch->writefd.revents = 0;
3110 g_source_remove_poll ((GSource *) watch, &watch->writefd);
3111 keep_running = (watch->readfd.events != 0);
3114 if (watch->funcs.error_full)
3115 GST_RTSP_CHECK (watch->funcs.error_full (watch, res, NULL,
3116 watch->write_id, watch->user_data), error);
3124 if (watch->funcs.error)
3125 watch->funcs.error (watch, res, watch->user_data);
3127 return keep_running;
3132 gst_rtsp_rec_free (gpointer data)
3134 GstRTSPRec *rec = data;
3137 g_slice_free (GstRTSPRec, rec);
3141 gst_rtsp_source_finalize (GSource * source)
3143 GstRTSPWatch *watch = (GstRTSPWatch *) source;
3145 build_reset (&watch->builder);
3146 gst_rtsp_message_unset (&watch->message);
3148 g_queue_foreach (watch->messages, (GFunc) gst_rtsp_rec_free, NULL);
3149 g_queue_free (watch->messages);
3150 watch->messages = NULL;
3151 g_free (watch->write_data);
3153 g_mutex_free (watch->mutex);
3156 watch->notify (watch->user_data);
3159 static GSourceFuncs gst_rtsp_source_funcs = {
3160 gst_rtsp_source_prepare,
3161 gst_rtsp_source_check,
3162 gst_rtsp_source_dispatch,
3163 gst_rtsp_source_finalize,
3169 * gst_rtsp_watch_new:
3170 * @conn: a #GstRTSPConnection
3171 * @funcs: watch functions
3172 * @user_data: user data to pass to @funcs
3173 * @notify: notify when @user_data is not referenced anymore
3175 * Create a watch object for @conn. The functions provided in @funcs will be
3176 * called with @user_data when activity happened on the watch.
3178 * The new watch is usually created so that it can be attached to a
3179 * maincontext with gst_rtsp_watch_attach().
3181 * @conn must exist for the entire lifetime of the watch.
3183 * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP
3184 * communication. Free with gst_rtsp_watch_unref () after usage.
3189 gst_rtsp_watch_new (GstRTSPConnection * conn,
3190 GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify)
3192 GstRTSPWatch *result;
3194 g_return_val_if_fail (conn != NULL, NULL);
3195 g_return_val_if_fail (funcs != NULL, NULL);
3196 g_return_val_if_fail (conn->read_socket != NULL, NULL);
3197 g_return_val_if_fail (conn->write_socket != NULL, NULL);
3199 result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs,
3200 sizeof (GstRTSPWatch));
3202 result->conn = conn;
3203 result->builder.state = STATE_START;
3205 result->mutex = g_mutex_new ();
3206 result->messages = g_queue_new ();
3208 result->readfd.fd = -1;
3209 result->writefd.fd = -1;
3211 gst_rtsp_watch_reset (result);
3213 result->funcs = *funcs;
3214 result->user_data = user_data;
3215 result->notify = notify;
3221 * gst_rtsp_watch_reset:
3222 * @watch: a #GstRTSPWatch
3224 * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel()
3225 * when the file descriptors of the connection might have changed.
3230 gst_rtsp_watch_reset (GstRTSPWatch * watch)
3232 if (watch->readfd.fd != -1)
3233 g_source_remove_poll ((GSource *) watch, &watch->readfd);
3234 if (watch->writefd.fd != -1)
3235 g_source_remove_poll ((GSource *) watch, &watch->writefd);
3237 watch->readfd.fd = g_socket_get_fd (watch->conn->read_socket);
3238 watch->readfd.events = READ_COND;
3239 watch->readfd.revents = 0;
3241 watch->writefd.fd = g_socket_get_fd (watch->conn->write_socket);
3242 watch->writefd.events = WRITE_ERR;
3243 watch->writefd.revents = 0;
3245 if (watch->readfd.fd != -1)
3246 g_source_add_poll ((GSource *) watch, &watch->readfd);
3247 if (watch->writefd.fd != -1)
3248 g_source_add_poll ((GSource *) watch, &watch->writefd);
3252 * gst_rtsp_watch_attach:
3253 * @watch: a #GstRTSPWatch
3254 * @context: a GMainContext (if NULL, the default context will be used)
3256 * Adds a #GstRTSPWatch to a context so that it will be executed within that context.
3258 * Returns: the ID (greater than 0) for the watch within the GMainContext.
3263 gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
3265 g_return_val_if_fail (watch != NULL, 0);
3267 return g_source_attach ((GSource *) watch, context);
3271 * gst_rtsp_watch_unref:
3272 * @watch: a #GstRTSPWatch
3274 * Decreases the reference count of @watch by one. If the resulting reference
3275 * count is zero the watch and associated memory will be destroyed.
3280 gst_rtsp_watch_unref (GstRTSPWatch * watch)
3282 g_return_if_fail (watch != NULL);
3284 g_source_unref ((GSource *) watch);
3288 * gst_rtsp_watch_write_data:
3289 * @watch: a #GstRTSPWatch
3290 * @data: the data to queue
3291 * @size: the size of @data
3292 * @id: location for a message ID or %NULL
3294 * Write @data using the connection of the @watch. If it cannot be sent
3295 * immediately, it will be queued for transmission in @watch. The contents of
3296 * @message will then be serialized and transmitted when the connection of the
3297 * @watch becomes writable. In case the @message is queued, the ID returned in
3298 * @id will be non-zero and used as the ID argument in the message_sent
3301 * This function will take ownership of @data and g_free() it after use.
3303 * Returns: #GST_RTSP_OK on success.
3308 gst_rtsp_watch_write_data (GstRTSPWatch * watch, const guint8 * data,
3309 guint size, guint * id)
3314 GMainContext *context = NULL;
3316 g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3317 g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
3318 g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
3320 g_mutex_lock (watch->mutex);
3322 /* try to send the message synchronously first */
3323 if (watch->messages->length == 0) {
3325 write_bytes (watch->conn->write_socket, data, &off, size,
3326 watch->conn->cancellable);
3327 if (res != GST_RTSP_EINTR) {
3330 g_free ((gpointer) data);
3335 /* make a record with the data and id for sending async */
3336 rec = g_slice_new (GstRTSPRec);
3338 rec->data = (guint8 *) data;
3341 rec->data = g_memdup (data + off, size - off);
3342 rec->size = size - off;
3343 g_free ((gpointer) data);
3347 /* make sure rec->id is never 0 */
3348 rec->id = ++watch->id;
3349 } while (G_UNLIKELY (rec->id == 0));
3351 /* add the record to a queue. FIXME we would like to have an upper limit here */
3352 g_queue_push_head (watch->messages, rec);
3354 /* make sure the main context will now also check for writability on the
3356 if (watch->writefd.events != WRITE_COND) {
3357 watch->writefd.events = WRITE_COND;
3358 context = ((GSource *) watch)->context;
3366 g_mutex_unlock (watch->mutex);
3369 g_main_context_wakeup (context);
3375 * gst_rtsp_watch_send_message:
3376 * @watch: a #GstRTSPWatch
3377 * @message: a #GstRTSPMessage
3378 * @id: location for a message ID or %NULL
3380 * Send a @message using the connection of the @watch. If it cannot be sent
3381 * immediately, it will be queued for transmission in @watch. The contents of
3382 * @message will then be serialized and transmitted when the connection of the
3383 * @watch becomes writable. In case the @message is queued, the ID returned in
3384 * @id will be non-zero and used as the ID argument in the message_sent
3387 * Returns: #GST_RTSP_OK on success.
3392 gst_rtsp_watch_send_message (GstRTSPWatch * watch, GstRTSPMessage * message,
3398 g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3399 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3401 /* make a record with the message as a string and id */
3402 str = message_to_string (watch->conn, message);
3404 return gst_rtsp_watch_write_data (watch,
3405 (guint8 *) g_string_free (str, FALSE), size, id);