summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/ipc/rtc_tcp_connection.cpp
blob: 1cc800b7d77e2b69308184a165b27fa896162a59 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// license:BSD-3-Clause
// copyright-holders:Inaki Baz Castillo,Miodrag Milanovic
#include "emu.h"
#include "rtc_tcp_connection.h"

/* Instance methods. */

rtc_tcp_connection::rtc_tcp_connection(listener* listener, size_t bufferSize) :
	tcp_connection(bufferSize),
	m_listener(listener),
	m_frame_start(0)
{
}

rtc_tcp_connection::~rtc_tcp_connection()
{
}

inline uint16_t get2bytes(const uint8_t* data, size_t i)
{
	return (uint16_t)(data[i + 1]) | ((uint16_t)(data[i])) << 8;
}

inline void set2bytes(uint8_t* data, size_t i, uint16_t value)
{
	data[i + 1] = (uint8_t)(value);
	data[i] = (uint8_t)(value >> 8);
}


void rtc_tcp_connection::user_on_tcp_connection_read()
{
	/*
	 * Framing RFC 4571
	 *
	 *     0                   1                   2                   3
	 *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *     ---------------------------------------------------------------
	 *     |             LENGTH            |  STUN / DTLS / RTP / RTCP   |
	 *     ---------------------------------------------------------------
	 *
	 * A 16-bit unsigned integer LENGTH field, coded in network byte order
	 * (big-endian), begins the frame.  If LENGTH is non-zero, an RTP or
	 * RTCP packet follows the LENGTH field.  The value coded in the LENGTH
	 * field MUST equal the number of octets in the RTP or RTCP packet.
	 * Zero is a valid value for LENGTH, and it codes the null packet.
	 */

	// Be ready to parse more than a single frame in a single TCP chunk.
	while (true)
	{
		// We may receive multiple packets in the same TCP chunk. If one of them is
		// a DTLS Close Alert this would be closed (Close() called) so we cannot call
		// our listeners anymore.
		if (is_closing())
			return;

		size_t data_len = m_buffer_data_len - m_frame_start;
		size_t packet_len = 0;

		if (data_len >= 2)
			packet_len = (size_t)get2bytes(m_buffer + m_frame_start, 0);

		// We have packet_len bytes.
		if ((data_len >= 2) && data_len >= 2 + packet_len)
		{
			const uint8_t* packet = m_buffer + m_frame_start + 2;

			// Notify the listener.
			if (packet_len != 0)
			{
				m_listener->on_packet_recv(this, packet, packet_len);
			}

			// If there is no more space available in the buffer and that is because
			// the latest parsed frame filled it, then empty the full buffer.
			if ((m_frame_start + 2 + packet_len) == m_buffer_size)
			{
				osd_printf_error("no more space in the buffer, emptying the buffer data");

				m_frame_start = 0;
				m_buffer_data_len = 0;
			}
			// If there is still space in the buffer, set the beginning of the next
			// frame to the next position after the parsed frame.
			else
			{
				m_frame_start += 2 + packet_len;
			}

			// If there is more data in the buffer after the parsed frame then
			// parse again. Otherwise break here and wait for more data.
			if (m_buffer_data_len > m_frame_start)
			{
				// osd_printf_error("there is more data after the parsed frame, continue parsing");

				continue;
			}
			else
			{
				break;
			}
		}		
		else // Incomplete packet.
		{
			// Check if the buffer is full.
			if (m_buffer_data_len == m_buffer_size)
			{
				// First case: the incomplete frame does not begin at position 0 of
				// the buffer, so move the frame to the position 0.
				if (m_frame_start != 0)
				{
					// osd_printf_error("no more space in the buffer, moving parsed bytes to the beginning of the buffer and wait for more data");

					std::memmove(m_buffer, m_buffer + m_frame_start, m_buffer_size - m_frame_start);
					m_buffer_data_len = m_buffer_size - m_frame_start;
					m_frame_start = 0;
				}
				// Second case: the incomplete frame begins at position 0 of the buffer.
				// The frame is too big, so close the connection.
				else
				{
					osd_printf_error("no more space in the buffer for the unfinished frame being parsed, closing the connection");

					// Close the socket.
					close();
				}
			}
			// The buffer is not full.
			else
			{
				osd_printf_verbose("frame not finished yet, waiting for more data");
			}

			// Exit the parsing loop.
			break;
		}
	}
}

void rtc_tcp_connection::send(const uint8_t* data, size_t len)
{
	// Write according to Framing RFC 4571.

	uint8_t frame_len[2];

	set2bytes(frame_len, 0, len);

	write(frame_len, 2, data, len);
}