2 * Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
3 * Copyright (C) 2010 Tim-Philipp Müller <tim centricular net>
4 * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk>
5 * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
22 /* THIS IS A PRIVATE API
23 * SECTION:gstntppacket
24 * @short_description: Helper structure to construct clock packets used
25 * by network clocks for NTPv4.
26 * @see_also: #GstClock, #GstNetClientClock, #GstNtpClock
28 * Various functions for receiving, sending an serializing #GstNtpPacket
32 /* FIXME 2.0: Merge this with GstNetTimePacket! */
48 #include "gstntppacket.h"
50 G_DEFINE_BOXED_TYPE (GstNtpPacket, gst_ntp_packet,
51 gst_ntp_packet_copy, gst_ntp_packet_free);
54 static inline GstClockTime
55 ntp_timestamp_to_gst_clock_time (guint32 seconds, guint32 fraction)
57 return gst_util_uint64_scale (seconds, GST_SECOND, 1) +
58 gst_util_uint64_scale (fraction, GST_SECOND,
59 G_GUINT64_CONSTANT (1) << 32);
63 gst_clock_time_to_ntp_timestamp_seconds (GstClockTime gst)
65 GstClockTime seconds = gst_util_uint64_scale (gst, 1, GST_SECOND);
71 gst_clock_time_to_ntp_timestamp_fraction (GstClockTime gst)
73 GstClockTime seconds = gst_util_uint64_scale (gst, 1, GST_SECOND);
75 return gst_util_uint64_scale (gst - seconds, G_GUINT64_CONSTANT (1) << 32,
81 * @buffer: (array): a buffer from which to construct the packet, or NULL
84 * Creates a new #GstNtpPacket from a buffer received over the network. The
85 * caller is responsible for ensuring that @buffer is at least
86 * #GST_NTP_PACKET_SIZE bytes long.
88 * If @buffer is %NULL, the local and remote times will be set to
89 * #GST_CLOCK_TIME_NONE.
91 * MT safe. Caller owns return value (gst_ntp_packet_free to free).
93 * Returns: The new #GstNtpPacket.
96 gst_ntp_packet_new (const guint8 * buffer, GError ** error)
100 g_assert (sizeof (GstClockTime) == 8);
103 guint8 version = (buffer[0] >> 3) & 0x7;
104 guint8 stratum = buffer[1];
105 gint8 poll_interval = buffer[2];
108 g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_WRONG_VERSION,
109 "Invalid NTP version %d", version);
113 /* Kiss-o'-Death packet! */
115 gchar code[5] = { buffer[3 * 4 + 0], buffer[3 * 4 + 1], buffer[3 * 4 + 2],
119 /* AUTH, AUTO, CRYP, DENY, RSTR, NKEY => DENY */
120 if (strcmp (code, "AUTH") == 0 ||
121 strcmp (code, "AUTO") == 0 ||
122 strcmp (code, "CRYP") == 0 ||
123 strcmp (code, "DENY") == 0 ||
124 strcmp (code, "RSTR") == 0 || strcmp (code, "NKEY") == 0) {
125 g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_DENY,
126 "Kiss-o'-Death denied '%s'", code);
127 } else if (strcmp (code, "RATE") == 0) {
128 g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_RATE,
129 "Kiss-o'-Death '%s'", code);
131 g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_UNKNOWN,
132 "Kiss-o'-Death unknown '%s'", code);
138 ret = g_new0 (GstNtpPacket, 1);
140 ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 6 * 4),
141 GST_READ_UINT32_BE (buffer + 7 * 4));
143 ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 8 * 4),
144 GST_READ_UINT32_BE (buffer + 9 * 4));
146 ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 10 * 4),
147 GST_READ_UINT32_BE (buffer + 11 * 4));
149 /* Wireshark considers everything >= 3 as invalid */
150 if (poll_interval >= 3)
151 ret->poll_interval = GST_CLOCK_TIME_NONE;
152 else if (poll_interval >= 0)
153 ret->poll_interval = GST_SECOND << poll_interval;
155 ret->poll_interval = GST_SECOND >> (-poll_interval);
157 ret = g_new0 (GstNtpPacket, 1);
158 ret->origin_time = 0;
159 ret->receive_time = 0;
160 ret->transmit_time = 0;
161 ret->poll_interval = 0;
168 * gst_ntp_packet_free:
169 * @packet: the #GstNtpPacket
174 gst_ntp_packet_free (GstNtpPacket * packet)
180 * gst_ntp_packet_copy:
181 * @packet: the #GstNtpPacket
183 * Make a copy of @packet.
185 * Returns: a copy of @packet, free with gst_ntp_packet_free().
188 gst_ntp_packet_copy (const GstNtpPacket * packet)
192 ret = g_new0 (GstNtpPacket, 1);
193 ret->origin_time = packet->origin_time;
194 ret->receive_time = packet->receive_time;
195 ret->transmit_time = packet->transmit_time;
201 * gst_ntp_packet_serialize:
202 * @packet: the #GstNtpPacket
204 * Serialized a #GstNtpPacket into a newly-allocated sequence of
205 * #GST_NTP_PACKET_SIZE bytes, in network byte order. The value returned is
206 * suitable for passing to write(2) or sendto(2) for communication over the
209 * MT safe. Caller owns return value (g_free to free).
211 * Returns: A newly allocated sequence of #GST_NTP_PACKET_SIZE bytes.
214 gst_ntp_packet_serialize (const GstNtpPacket * packet)
218 g_assert (sizeof (GstClockTime) == 8);
220 ret = g_new0 (guint8, GST_NTP_PACKET_SIZE);
221 /* Leap Indicator: unknown
225 ret[0] = (3 << 6) | (4 << 3) | (3 << 0);
226 /* Stratum: unsynchronized */
228 /* Polling interval: invalid */
233 GST_WRITE_UINT32_BE (ret + 4, 0);
234 /* Root disperson: 0 */
235 GST_WRITE_UINT32_BE (ret + 2 * 4, 0);
236 /* Reference ID: \0 */
237 GST_WRITE_UINT32_BE (ret + 3 * 4, 0);
238 /* Reference Timestamp: 0 */
239 GST_WRITE_UINT32_BE (ret + 4 * 4, 0);
240 GST_WRITE_UINT32_BE (ret + 5 * 4, 0);
241 /* Origin timestamp (local time) */
242 GST_WRITE_UINT32_BE (ret + 6 * 4,
243 gst_clock_time_to_ntp_timestamp_seconds (packet->origin_time));
244 GST_WRITE_UINT32_BE (ret + 7 * 4,
245 gst_clock_time_to_ntp_timestamp_fraction (packet->origin_time));
246 /* Receive timestamp (remote time) */
247 GST_WRITE_UINT32_BE (ret + 8 * 4,
248 gst_clock_time_to_ntp_timestamp_seconds (packet->receive_time));
249 GST_WRITE_UINT32_BE (ret + 9 * 4,
250 gst_clock_time_to_ntp_timestamp_fraction (packet->receive_time));
251 /* Transmit timestamp (remote time) */
252 GST_WRITE_UINT32_BE (ret + 10 * 4,
253 gst_clock_time_to_ntp_timestamp_seconds (packet->transmit_time));
254 GST_WRITE_UINT32_BE (ret + 11 * 4,
255 gst_clock_time_to_ntp_timestamp_fraction (packet->transmit_time));
261 * gst_ntp_packet_receive:
262 * @socket: socket to receive the time packet on
263 * @src_address: (out): address of variable to return sender address
264 * @error: return address for a #GError, or NULL
266 * Receives a #GstNtpPacket over a socket. Handles interrupted system
267 * calls, but otherwise returns NULL on error.
269 * Returns: (transfer full): a new #GstNtpPacket, or NULL on error. Free
270 * with gst_ntp_packet_free() when done.
273 gst_ntp_packet_receive (GSocket * socket,
274 GSocketAddress ** src_address, GError ** error)
276 gchar buffer[GST_NTP_PACKET_SIZE];
280 g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
281 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
284 ret = g_socket_receive_from (socket, src_address, buffer,
285 GST_NTP_PACKET_SIZE, NULL, &err);
288 if (err->code == G_IO_ERROR_WOULD_BLOCK) {
295 } else if (ret < GST_NTP_PACKET_SIZE) {
298 return gst_ntp_packet_new ((const guint8 *) buffer, error);
304 GST_DEBUG ("receive error: %s", err->message);
305 g_propagate_error (error, err);
310 GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)",
311 ret, GST_NTP_PACKET_SIZE);
312 g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
313 "short time packet (%d < %d)", (int) ret, GST_NTP_PACKET_SIZE);
319 * gst_ntp_packet_send:
320 * @packet: the #GstNtpPacket to send
321 * @socket: socket to send the time packet on
322 * @dest_address: address to send the time packet to
323 * @error: return address for a #GError, or NULL
325 * Sends a #GstNtpPacket over a socket.
329 * Returns: TRUE if successful, FALSE in case an error occurred.
332 gst_ntp_packet_send (const GstNtpPacket * packet,
333 GSocket * socket, GSocketAddress * dest_address, GError ** error)
335 gboolean was_blocking;
339 g_return_val_if_fail (packet != NULL, FALSE);
340 g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
341 g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE);
342 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
344 was_blocking = g_socket_get_blocking (socket);
347 g_socket_set_blocking (socket, FALSE);
349 /* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */
350 buffer = gst_ntp_packet_serialize (packet);
352 res = g_socket_send_to (socket, dest_address, (const gchar *) buffer,
353 GST_NTP_PACKET_SIZE, NULL, error);
355 /* datagram packets should be sent as a whole or not at all */
356 g_assert (res < 0 || res == GST_NTP_PACKET_SIZE);
361 g_socket_set_blocking (socket, TRUE);
363 return (res == GST_NTP_PACKET_SIZE);
367 gst_ntp_error_quark (void)
371 /* Thread-safe because GQuark is */
373 quark = g_quark_from_static_string ("gst-ntp-error-quark");