1 /* GStreamer RTMP Library
2 * Copyright (C) 2017 Make.TV, Inc. <info@make.tv>
3 * Contact: Jan Alexander Steffens (heftig) <jsteffens@make.tv>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "rtmphandshake.h"
26 #include "rtmputils.h"
31 GST_DEBUG_CATEGORY_STATIC (gst_rtmp_handshake_debug_category);
32 #define GST_CAT_DEFAULT gst_rtmp_handshake_debug_category
37 static volatile gsize done = 0;
38 if (g_once_init_enter (&done)) {
39 GST_DEBUG_CATEGORY_INIT (gst_rtmp_handshake_debug_category, "rtmphandshake",
40 0, "debug category for the rtmp connection handshake");
41 g_once_init_leave (&done, 1);
45 static void client_handshake1_done (GObject * source, GAsyncResult * result,
47 static void client_handshake2_done (GObject * source, GAsyncResult * result,
49 static void client_handshake3_done (GObject * source, GAsyncResult * result,
53 serialize_u8 (GByteArray * array, guint8 value)
55 g_byte_array_append (array, (guint8 *) & value, sizeof value);
59 serialize_u32 (GByteArray * array, guint32 value)
61 value = GUINT32_TO_BE (value);
62 g_byte_array_append (array, (guint8 *) & value, sizeof value);
67 #define SIZE_P2 SIZE_P1
68 #define SIZE_P0P1 (SIZE_P0 + SIZE_P1)
69 #define SIZE_P0P1P2 (SIZE_P0P1 + SIZE_P2)
70 #define SIZE_RANDOM (SIZE_P1 - 8)
79 handshake_random_data (void)
81 G_STATIC_ASSERT (SIZE_RANDOM % 4 == 0);
83 GByteArray *ba = g_byte_array_sized_new (SIZE_RANDOM);
86 for (i = 0; i < SIZE_RANDOM; i += 4) {
87 serialize_u32 (ba, g_random_int ());
90 return g_byte_array_free_to_bytes (ba);
93 static HandshakeData *
94 handshake_data_new (gboolean strict)
96 HandshakeData *data = g_slice_new0 (HandshakeData);
97 data->random_bytes = handshake_random_data ();
98 data->strict = strict;
103 handshake_data_free (gpointer ptr)
105 HandshakeData *data = ptr;
106 g_clear_pointer (&data->random_bytes, g_bytes_unref);
107 g_slice_free (HandshakeData, data);
111 handshake_data_check (HandshakeData * data, const guint8 * p2)
113 const guint8 *ourrandom = g_bytes_get_data (data->random_bytes, NULL);
114 return memcmp (ourrandom, p2 + 8, SIZE_P2 - 8) == 0;
118 create_c0c1 (GBytes * random_bytes)
120 GByteArray *ba = g_byte_array_sized_new (SIZE_P0P1);
123 serialize_u8 (ba, 3);
126 serialize_u32 (ba, g_get_monotonic_time () / 1000);
129 serialize_u32 (ba, 0);
132 gst_rtmp_byte_array_append_bytes (ba, random_bytes);
134 GST_DEBUG ("Sending C0+C1");
135 GST_MEMDUMP (">>> C0", ba->data, SIZE_P0);
136 GST_MEMDUMP (">>> C1", ba->data + SIZE_P0, SIZE_P1);
138 return g_byte_array_free_to_bytes (ba);
142 gst_rtmp_client_handshake (GIOStream * stream, gboolean strict,
143 GCancellable * cancellable, GAsyncReadyCallback callback,
149 g_return_if_fail (G_IS_IO_STREAM (stream));
152 GST_INFO ("Starting client handshake");
154 task = g_task_new (stream, cancellable, callback, user_data);
155 data = handshake_data_new (strict);
156 g_task_set_task_data (task, data, handshake_data_free);
159 GOutputStream *os = g_io_stream_get_output_stream (stream);
160 GBytes *bytes = create_c0c1 (data->random_bytes);
162 gst_rtmp_output_stream_write_all_bytes_async (os,
163 bytes, G_PRIORITY_DEFAULT,
164 g_task_get_cancellable (task), client_handshake1_done, task);
166 g_bytes_unref (bytes);
171 client_handshake1_done (GObject * source, GAsyncResult * result,
174 GOutputStream *os = G_OUTPUT_STREAM (source);
175 GTask *task = user_data;
176 GIOStream *stream = g_task_get_source_object (task);
177 GInputStream *is = g_io_stream_get_input_stream (stream);
178 GError *error = NULL;
181 res = gst_rtmp_output_stream_write_all_bytes_finish (os, result, &error);
183 GST_ERROR ("Failed to send C0+C1: %s", error->message);
184 g_task_return_error (task, error);
185 g_object_unref (task);
189 GST_DEBUG ("Sent C0+C1, waiting for S0+S1+S2");
190 gst_rtmp_input_stream_read_all_bytes_async (is, SIZE_P0P1P2,
191 G_PRIORITY_DEFAULT, g_task_get_cancellable (task),
192 client_handshake2_done, task);
196 create_c2 (const guint8 * s0s1s2)
198 G_STATIC_ASSERT (SIZE_P1 == SIZE_P2);
200 GByteArray *ba = g_byte_array_sized_new (SIZE_P2);
201 gint64 c2time = g_get_monotonic_time ();
204 g_byte_array_set_size (ba, SIZE_P2);
205 memcpy (ba->data, s0s1s2 + SIZE_P0, SIZE_P1);
208 GST_WRITE_UINT32_BE (ba->data + 4, c2time / 1000);
210 GST_DEBUG ("Sending C2");
211 GST_MEMDUMP (">>> C2", ba->data, SIZE_P2);
213 return g_byte_array_free_to_bytes (ba);
217 client_handshake2_done (GObject * source, GAsyncResult * result,
220 GInputStream *is = G_INPUT_STREAM (source);
221 GTask *task = user_data;
222 GIOStream *stream = g_task_get_source_object (task);
223 HandshakeData *data = g_task_get_task_data (task);
224 GError *error = NULL;
226 const guint8 *s0s1s2;
229 res = gst_rtmp_input_stream_read_all_bytes_finish (is, result, &error);
231 GST_ERROR ("Failed to read S0+S1+S2: %s", error->message);
232 g_task_return_error (task, error);
233 g_object_unref (task);
237 s0s1s2 = g_bytes_get_data (res, &size);
238 if (size < SIZE_P0P1P2) {
239 GST_ERROR ("Short read (want %d have %" G_GSIZE_FORMAT ")", SIZE_P0P1P2,
241 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
242 "Short read (want %d have %" G_GSIZE_FORMAT ")", SIZE_P0P1P2, size);
243 g_object_unref (task);
247 GST_DEBUG ("Got S0+S1+S2");
248 GST_MEMDUMP ("<<< S0", s0s1s2, SIZE_P0);
249 GST_MEMDUMP ("<<< S1", s0s1s2 + SIZE_P0, SIZE_P1);
250 GST_MEMDUMP ("<<< S2", s0s1s2 + SIZE_P0P1, SIZE_P2);
252 if (handshake_data_check (data, s0s1s2 + SIZE_P0P1)) {
253 GST_DEBUG ("S2 random data matches C1");
256 GST_ERROR ("Handshake response data did not match");
257 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
258 "Handshake response data did not match");
259 g_object_unref (task);
263 GST_WARNING ("Handshake reponse data did not match; continuing anyway");
267 GOutputStream *os = g_io_stream_get_output_stream (stream);
268 GBytes *bytes = create_c2 (s0s1s2);
270 gst_rtmp_output_stream_write_all_bytes_async (os,
271 bytes, G_PRIORITY_DEFAULT,
272 g_task_get_cancellable (task), client_handshake3_done, task);
274 g_bytes_unref (bytes);
282 client_handshake3_done (GObject * source, GAsyncResult * result,
285 GOutputStream *os = G_OUTPUT_STREAM (source);
286 GTask *task = user_data;
287 GError *error = NULL;
290 res = gst_rtmp_output_stream_write_all_bytes_finish (os, result, &error);
292 GST_ERROR ("Failed to send C2: %s", error->message);
293 g_task_return_error (task, error);
294 g_object_unref (task);
298 GST_DEBUG ("Sent C2");
299 GST_INFO ("Client handshake finished");
301 g_task_return_boolean (task, TRUE);
302 g_object_unref (task);
306 gst_rtmp_client_handshake_finish (GIOStream * stream, GAsyncResult * result,
309 g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
310 return g_task_propagate_boolean (G_TASK (result), error);