1 | /**************************************************************************/ |
2 | /* packet_peer_udp.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 "packet_peer_udp.h" |
32 | |
33 | #include "core/io/ip.h" |
34 | #include "core/io/udp_server.h" |
35 | |
36 | void PacketPeerUDP::set_blocking_mode(bool p_enable) { |
37 | blocking = p_enable; |
38 | } |
39 | |
40 | void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { |
41 | ERR_FAIL_COND(udp_server); |
42 | broadcast = p_enabled; |
43 | if (_sock.is_valid() && _sock->is_open()) { |
44 | _sock->set_broadcasting_enabled(p_enabled); |
45 | } |
46 | } |
47 | |
48 | Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, String p_if_name) { |
49 | ERR_FAIL_COND_V(udp_server, ERR_LOCKED); |
50 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
51 | ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); |
52 | |
53 | if (!_sock->is_open()) { |
54 | IP::Type ip_type = p_multi_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; |
55 | Error err = _sock->open(NetSocket::TYPE_UDP, ip_type); |
56 | ERR_FAIL_COND_V(err != OK, err); |
57 | _sock->set_blocking_enabled(false); |
58 | _sock->set_broadcasting_enabled(broadcast); |
59 | } |
60 | return _sock->join_multicast_group(p_multi_address, p_if_name); |
61 | } |
62 | |
63 | Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, String p_if_name) { |
64 | ERR_FAIL_COND_V(udp_server, ERR_LOCKED); |
65 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
66 | ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); |
67 | return _sock->leave_multicast_group(p_multi_address, p_if_name); |
68 | } |
69 | |
70 | String PacketPeerUDP::_get_packet_ip() const { |
71 | return get_packet_address(); |
72 | } |
73 | |
74 | Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { |
75 | IPAddress ip; |
76 | if (p_address.is_valid_ip_address()) { |
77 | ip = p_address; |
78 | } else { |
79 | ip = IP::get_singleton()->resolve_hostname(p_address); |
80 | if (!ip.is_valid()) { |
81 | return ERR_CANT_RESOLVE; |
82 | } |
83 | } |
84 | |
85 | set_dest_address(ip, p_port); |
86 | return OK; |
87 | } |
88 | |
89 | int PacketPeerUDP::get_available_packet_count() const { |
90 | // TODO we should deprecate this, and expose poll instead! |
91 | Error err = const_cast<PacketPeerUDP *>(this)->_poll(); |
92 | if (err != OK) { |
93 | return -1; |
94 | } |
95 | |
96 | return queue_count; |
97 | } |
98 | |
99 | Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { |
100 | Error err = _poll(); |
101 | if (err != OK) { |
102 | return err; |
103 | } |
104 | if (queue_count == 0) { |
105 | return ERR_UNAVAILABLE; |
106 | } |
107 | |
108 | uint32_t size = 0; |
109 | uint8_t ipv6[16]; |
110 | rb.read(ipv6, 16, true); |
111 | packet_ip.set_ipv6(ipv6); |
112 | rb.read((uint8_t *)&packet_port, 4, true); |
113 | rb.read((uint8_t *)&size, 4, true); |
114 | rb.read(packet_buffer, size, true); |
115 | --queue_count; |
116 | *r_buffer = packet_buffer; |
117 | r_buffer_size = size; |
118 | return OK; |
119 | } |
120 | |
121 | Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { |
122 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
123 | ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); |
124 | |
125 | Error err; |
126 | int sent = -1; |
127 | |
128 | if (!_sock->is_open()) { |
129 | IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; |
130 | err = _sock->open(NetSocket::TYPE_UDP, ip_type); |
131 | ERR_FAIL_COND_V(err != OK, err); |
132 | _sock->set_blocking_enabled(false); |
133 | _sock->set_broadcasting_enabled(broadcast); |
134 | } |
135 | |
136 | do { |
137 | if (connected && !udp_server) { |
138 | err = _sock->send(p_buffer, p_buffer_size, sent); |
139 | } else { |
140 | err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); |
141 | } |
142 | if (err != OK) { |
143 | if (err != ERR_BUSY) { |
144 | return FAILED; |
145 | } else if (!blocking) { |
146 | return ERR_BUSY; |
147 | } |
148 | // Keep trying to send full packet |
149 | continue; |
150 | } |
151 | return OK; |
152 | |
153 | } while (sent != p_buffer_size); |
154 | |
155 | return OK; |
156 | } |
157 | |
158 | int PacketPeerUDP::get_max_packet_size() const { |
159 | return 512; // uhm maybe not |
160 | } |
161 | |
162 | Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_recv_buffer_size) { |
163 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
164 | ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); |
165 | ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); |
166 | ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)." ); |
167 | |
168 | Error err; |
169 | IP::Type ip_type = IP::TYPE_ANY; |
170 | |
171 | if (p_bind_address.is_valid()) { |
172 | ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; |
173 | } |
174 | |
175 | err = _sock->open(NetSocket::TYPE_UDP, ip_type); |
176 | |
177 | if (err != OK) { |
178 | return ERR_CANT_CREATE; |
179 | } |
180 | |
181 | _sock->set_blocking_enabled(false); |
182 | _sock->set_broadcasting_enabled(broadcast); |
183 | err = _sock->bind(p_bind_address, p_port); |
184 | |
185 | if (err != OK) { |
186 | _sock->close(); |
187 | return err; |
188 | } |
189 | rb.resize(nearest_shift(p_recv_buffer_size)); |
190 | return OK; |
191 | } |
192 | |
193 | Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IPAddress p_ip, uint16_t p_port, UDPServer *p_server) { |
194 | udp_server = p_server; |
195 | connected = true; |
196 | _sock = p_sock; |
197 | peer_addr = p_ip; |
198 | peer_port = p_port; |
199 | packet_ip = peer_addr; |
200 | packet_port = peer_port; |
201 | return OK; |
202 | } |
203 | |
204 | void PacketPeerUDP::disconnect_shared_socket() { |
205 | udp_server = nullptr; |
206 | _sock = Ref<NetSocket>(NetSocket::create()); |
207 | close(); |
208 | } |
209 | |
210 | Error PacketPeerUDP::connect_to_host(const IPAddress &p_host, int p_port) { |
211 | ERR_FAIL_COND_V(udp_server, ERR_LOCKED); |
212 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
213 | ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); |
214 | ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive)." ); |
215 | |
216 | Error err; |
217 | |
218 | if (!_sock->is_open()) { |
219 | IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; |
220 | err = _sock->open(NetSocket::TYPE_UDP, ip_type); |
221 | ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); |
222 | _sock->set_blocking_enabled(false); |
223 | } |
224 | |
225 | err = _sock->connect_to_host(p_host, p_port); |
226 | |
227 | // I see no reason why we should get ERR_BUSY (wouldblock/eagain) here. |
228 | // This is UDP, so connect is only used to tell the OS to which socket |
229 | // it should deliver packets when multiple are bound on the same address/port. |
230 | if (err != OK) { |
231 | close(); |
232 | ERR_FAIL_V_MSG(FAILED, "Unable to connect" ); |
233 | } |
234 | |
235 | connected = true; |
236 | |
237 | peer_addr = p_host; |
238 | peer_port = p_port; |
239 | |
240 | // Flush any packet we might still have in queue. |
241 | rb.clear(); |
242 | return OK; |
243 | } |
244 | |
245 | bool PacketPeerUDP::is_socket_connected() const { |
246 | return connected; |
247 | } |
248 | |
249 | void PacketPeerUDP::close() { |
250 | if (udp_server) { |
251 | udp_server->remove_peer(peer_addr, peer_port); |
252 | udp_server = nullptr; |
253 | _sock = Ref<NetSocket>(NetSocket::create()); |
254 | } else if (_sock.is_valid()) { |
255 | _sock->close(); |
256 | } |
257 | rb.resize(16); |
258 | queue_count = 0; |
259 | connected = false; |
260 | } |
261 | |
262 | Error PacketPeerUDP::wait() { |
263 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
264 | return _sock->poll(NetSocket::POLL_TYPE_IN, -1); |
265 | } |
266 | |
267 | Error PacketPeerUDP::_poll() { |
268 | ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); |
269 | |
270 | if (!_sock->is_open()) { |
271 | return FAILED; |
272 | } |
273 | if (udp_server) { |
274 | return OK; // Handled by UDPServer. |
275 | } |
276 | |
277 | Error err; |
278 | int read; |
279 | IPAddress ip; |
280 | uint16_t port; |
281 | |
282 | while (true) { |
283 | if (connected) { |
284 | err = _sock->recv(recv_buffer, sizeof(recv_buffer), read); |
285 | ip = peer_addr; |
286 | port = peer_port; |
287 | } else { |
288 | err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); |
289 | } |
290 | |
291 | if (err != OK) { |
292 | if (err == ERR_BUSY) { |
293 | break; |
294 | } |
295 | return FAILED; |
296 | } |
297 | |
298 | err = store_packet(ip, port, recv_buffer, read); |
299 | #ifdef TOOLS_ENABLED |
300 | if (err != OK) { |
301 | WARN_PRINT("Buffer full, dropping packets!" ); |
302 | } |
303 | #endif |
304 | } |
305 | |
306 | return OK; |
307 | } |
308 | |
309 | Error PacketPeerUDP::store_packet(IPAddress p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) { |
310 | if (rb.space_left() < p_buf_size + 24) { |
311 | return ERR_OUT_OF_MEMORY; |
312 | } |
313 | rb.write(p_ip.get_ipv6(), 16); |
314 | rb.write((uint8_t *)&p_port, 4); |
315 | rb.write((uint8_t *)&p_buf_size, 4); |
316 | rb.write(p_buf, p_buf_size); |
317 | ++queue_count; |
318 | return OK; |
319 | } |
320 | |
321 | bool PacketPeerUDP::is_bound() const { |
322 | return _sock.is_valid() && _sock->is_open(); |
323 | } |
324 | |
325 | IPAddress PacketPeerUDP::get_packet_address() const { |
326 | return packet_ip; |
327 | } |
328 | |
329 | int PacketPeerUDP::get_packet_port() const { |
330 | return packet_port; |
331 | } |
332 | |
333 | int PacketPeerUDP::get_local_port() const { |
334 | uint16_t local_port; |
335 | _sock->get_socket_address(nullptr, &local_port); |
336 | return local_port; |
337 | } |
338 | |
339 | void PacketPeerUDP::set_dest_address(const IPAddress &p_address, int p_port) { |
340 | ERR_FAIL_COND_MSG(connected, "Destination address cannot be set for connected sockets" ); |
341 | peer_addr = p_address; |
342 | peer_port = p_port; |
343 | } |
344 | |
345 | void PacketPeerUDP::_bind_methods() { |
346 | ClassDB::bind_method(D_METHOD("bind" , "port" , "bind_address" , "recv_buf_size" ), &PacketPeerUDP::bind, DEFVAL("*" ), DEFVAL(65536)); |
347 | ClassDB::bind_method(D_METHOD("close" ), &PacketPeerUDP::close); |
348 | ClassDB::bind_method(D_METHOD("wait" ), &PacketPeerUDP::wait); |
349 | ClassDB::bind_method(D_METHOD("is_bound" ), &PacketPeerUDP::is_bound); |
350 | ClassDB::bind_method(D_METHOD("connect_to_host" , "host" , "port" ), &PacketPeerUDP::connect_to_host); |
351 | ClassDB::bind_method(D_METHOD("is_socket_connected" ), &PacketPeerUDP::is_socket_connected); |
352 | ClassDB::bind_method(D_METHOD("get_packet_ip" ), &PacketPeerUDP::_get_packet_ip); |
353 | ClassDB::bind_method(D_METHOD("get_packet_port" ), &PacketPeerUDP::get_packet_port); |
354 | ClassDB::bind_method(D_METHOD("get_local_port" ), &PacketPeerUDP::get_local_port); |
355 | ClassDB::bind_method(D_METHOD("set_dest_address" , "host" , "port" ), &PacketPeerUDP::_set_dest_address); |
356 | ClassDB::bind_method(D_METHOD("set_broadcast_enabled" , "enabled" ), &PacketPeerUDP::set_broadcast_enabled); |
357 | ClassDB::bind_method(D_METHOD("join_multicast_group" , "multicast_address" , "interface_name" ), &PacketPeerUDP::join_multicast_group); |
358 | ClassDB::bind_method(D_METHOD("leave_multicast_group" , "multicast_address" , "interface_name" ), &PacketPeerUDP::leave_multicast_group); |
359 | } |
360 | |
361 | PacketPeerUDP::PacketPeerUDP() : |
362 | _sock(Ref<NetSocket>(NetSocket::create())) { |
363 | rb.resize(16); |
364 | } |
365 | |
366 | PacketPeerUDP::~PacketPeerUDP() { |
367 | close(); |
368 | } |
369 | |