Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/mbedtls/stream_peer_mbedtls.cpp
10277 views
1
/**************************************************************************/
2
/* stream_peer_mbedtls.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "stream_peer_mbedtls.h"
32
33
#include "core/io/stream_peer_tcp.h"
34
35
int StreamPeerMbedTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
36
if (buf == nullptr || len == 0) {
37
return 0;
38
}
39
40
StreamPeerMbedTLS *sp = static_cast<StreamPeerMbedTLS *>(ctx);
41
42
ERR_FAIL_NULL_V(sp, 0);
43
44
int sent;
45
Error err = sp->base->put_partial_data((const uint8_t *)buf, len, sent);
46
if (err != OK) {
47
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
48
}
49
if (sent == 0) {
50
return MBEDTLS_ERR_SSL_WANT_WRITE;
51
}
52
return sent;
53
}
54
55
int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
56
if (buf == nullptr || len == 0) {
57
return 0;
58
}
59
60
StreamPeerMbedTLS *sp = static_cast<StreamPeerMbedTLS *>(ctx);
61
62
ERR_FAIL_NULL_V(sp, 0);
63
64
int got;
65
Error err = sp->base->get_partial_data((uint8_t *)buf, len, got);
66
if (err != OK) {
67
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
68
}
69
if (got == 0) {
70
return MBEDTLS_ERR_SSL_WANT_READ;
71
}
72
return got;
73
}
74
75
void StreamPeerMbedTLS::_cleanup() {
76
tls_ctx->clear();
77
base = Ref<StreamPeer>();
78
status = STATUS_DISCONNECTED;
79
}
80
81
Error StreamPeerMbedTLS::_do_handshake() {
82
int ret = mbedtls_ssl_handshake(tls_ctx->get_context());
83
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
84
// Handshake is still in progress, will retry via poll later.
85
return OK;
86
} else if (ret != 0) {
87
// An error occurred.
88
ERR_PRINT("TLS handshake error: " + itos(ret));
89
TLSContextMbedTLS::print_mbedtls_error(ret);
90
disconnect_from_stream();
91
status = STATUS_ERROR;
92
return FAILED;
93
}
94
95
status = STATUS_CONNECTED;
96
return OK;
97
}
98
99
Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options) {
100
ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);
101
102
Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, p_common_name, p_options.is_valid() ? p_options : TLSOptions::client());
103
ERR_FAIL_COND_V(err != OK, err);
104
105
base = p_base;
106
mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
107
108
status = STATUS_HANDSHAKING;
109
110
if (_do_handshake() != OK) {
111
status = STATUS_ERROR_HOSTNAME_MISMATCH;
112
return FAILED;
113
}
114
115
return OK;
116
}
117
118
Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options) {
119
ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);
120
ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER);
121
122
Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, p_options);
123
ERR_FAIL_COND_V(err != OK, err);
124
125
base = p_base;
126
127
mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
128
129
status = STATUS_HANDSHAKING;
130
131
if (_do_handshake() != OK) {
132
return FAILED;
133
}
134
135
status = STATUS_CONNECTED;
136
return OK;
137
}
138
139
Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
140
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
141
142
Error err;
143
int sent = 0;
144
145
while (p_bytes > 0) {
146
err = put_partial_data(p_data, p_bytes, sent);
147
148
if (err != OK) {
149
return err;
150
}
151
152
p_data += sent;
153
p_bytes -= sent;
154
}
155
156
return OK;
157
}
158
159
Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
160
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
161
162
r_sent = 0;
163
164
if (p_bytes == 0) {
165
return OK;
166
}
167
168
do {
169
int ret = mbedtls_ssl_write(tls_ctx->get_context(), &p_data[r_sent], p_bytes - r_sent);
170
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
171
// Non blocking IO.
172
break;
173
} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
174
// Clean close
175
disconnect_from_stream();
176
return ERR_FILE_EOF;
177
} else if (ret <= 0) {
178
TLSContextMbedTLS::print_mbedtls_error(ret);
179
disconnect_from_stream();
180
return ERR_CONNECTION_ERROR;
181
}
182
r_sent += ret;
183
184
} while (r_sent < p_bytes);
185
186
return OK;
187
}
188
189
Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
190
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
191
192
Error err;
193
194
int got = 0;
195
while (p_bytes > 0) {
196
err = get_partial_data(p_buffer, p_bytes, got);
197
198
if (err != OK) {
199
return err;
200
}
201
202
p_buffer += got;
203
p_bytes -= got;
204
}
205
206
return OK;
207
}
208
209
Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
210
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
211
212
r_received = 0;
213
214
do {
215
int ret = mbedtls_ssl_read(tls_ctx->get_context(), &p_buffer[r_received], p_bytes - r_received);
216
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
217
// Non blocking IO.
218
break;
219
} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
220
// Clean close
221
disconnect_from_stream();
222
return ERR_FILE_EOF;
223
} else if (ret <= 0) {
224
TLSContextMbedTLS::print_mbedtls_error(ret);
225
disconnect_from_stream();
226
return ERR_CONNECTION_ERROR;
227
}
228
229
r_received += ret;
230
231
} while (r_received < p_bytes);
232
233
return OK;
234
}
235
236
void StreamPeerMbedTLS::poll() {
237
ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING);
238
ERR_FAIL_COND(base.is_null());
239
240
if (status == STATUS_HANDSHAKING) {
241
_do_handshake();
242
return;
243
}
244
245
// We could pass nullptr as second parameter, but some behavior sanitizers don't seem to like that.
246
// Passing a 1 byte buffer to workaround it.
247
uint8_t byte;
248
int ret = mbedtls_ssl_read(tls_ctx->get_context(), &byte, 0);
249
250
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
251
// Nothing to read/write (non blocking IO)
252
} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
253
// Clean close (disconnect)
254
disconnect_from_stream();
255
return;
256
} else if (ret < 0) {
257
TLSContextMbedTLS::print_mbedtls_error(ret);
258
disconnect_from_stream();
259
return;
260
}
261
262
Ref<StreamPeerTCP> tcp = base;
263
if (tcp.is_valid() && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
264
disconnect_from_stream();
265
return;
266
}
267
}
268
269
int StreamPeerMbedTLS::get_available_bytes() const {
270
ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
271
272
return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls));
273
}
274
275
StreamPeerMbedTLS::StreamPeerMbedTLS() {
276
tls_ctx.instantiate();
277
}
278
279
StreamPeerMbedTLS::~StreamPeerMbedTLS() {
280
disconnect_from_stream();
281
}
282
283
void StreamPeerMbedTLS::disconnect_from_stream() {
284
if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING) {
285
return;
286
}
287
288
Ref<StreamPeerTCP> tcp = base;
289
if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
290
// We are still connected on the socket, try to send close notify.
291
mbedtls_ssl_close_notify(tls_ctx->get_context());
292
}
293
294
_cleanup();
295
}
296
297
StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
298
return status;
299
}
300
301
Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {
302
return base;
303
}
304
305
StreamPeerTLS *StreamPeerMbedTLS::_create_func(bool p_notify_postinitialize) {
306
return static_cast<StreamPeerTLS *>(ClassDB::creator<StreamPeerMbedTLS>(p_notify_postinitialize));
307
}
308
309
void StreamPeerMbedTLS::initialize_tls() {
310
_create = _create_func;
311
}
312
313
void StreamPeerMbedTLS::finalize_tls() {
314
_create = nullptr;
315
}
316
317