2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
9 * @file core/udp.cpp Basic functions to receive and send UDP packets.
12 #include "../../stdafx.h"
13 #include "../../date_func.h"
14 #include "../../debug.h"
15 #include "../../core/random_func.hpp"
18 #include "../../safeguards.h"
21 * Create an UDP socket but don't listen yet.
22 * @param bind the addresses to bind to.
24 NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList
*bind
)
26 if (bind
!= nullptr) {
27 for (NetworkAddress
&addr
: *bind
) {
28 this->bind
.push_back(addr
);
31 /* As an empty hostname and port 0 don't go well when
32 * resolving it we need to add an address for each of
33 * the address families we support. */
34 this->bind
.emplace_back("", 0, AF_INET
);
35 this->bind
.emplace_back("", 0, AF_INET6
);
38 this->fragment_token
= ((uint64_t) InteractiveRandom()) | (((uint64_t) InteractiveRandom()) << 32);
43 * Start listening on the given host and port.
44 * @return true if at least one port is listening
46 bool NetworkUDPSocketHandler::Listen()
48 /* Make sure socket is closed */
51 for (NetworkAddress
&addr
: this->bind
) {
52 addr
.Listen(SOCK_DGRAM
, &this->sockets
);
55 return !this->sockets
.empty();
59 * Close the actual UDP socket.
61 void NetworkUDPSocketHandler::CloseSocket()
63 for (auto &s
: this->sockets
) {
66 this->sockets
.clear();
70 * Send a packet over UDP
71 * @param p the packet to send
72 * @param recv the receiver (target) of the packet
73 * @param all send the packet using all sockets that can send it
74 * @param broadcast whether to send a broadcast message
76 void NetworkUDPSocketHandler::SendPacket(Packet
&p
, NetworkAddress
&recv
, bool all
, bool broadcast
, bool short_mtu
)
78 if (this->sockets
.empty()) this->Listen();
80 const uint MTU
= short_mtu
? UDP_MTU_SHORT
: UDP_MTU
;
85 uint64_t token
= this->fragment_token
++;
86 const uint PAYLOAD_MTU
= MTU
- (1 + 2 + 8 + 1 + 1 + 2);
88 const size_t packet_size
= p
.Size();
89 const uint8_t frag_count
= (uint8_t)((packet_size
+ PAYLOAD_MTU
- 1) / PAYLOAD_MTU
);
91 Packet
frag(this, PACKET_UDP_EX_MULTI
);
92 uint8_t current_frag
= 0;
94 while (offset
< packet_size
) {
95 uint16_t payload_size
= (uint16_t)std::min
<size_t>(PAYLOAD_MTU
, packet_size
- offset
);
96 frag
.Send_uint64(token
);
97 frag
.Send_uint8(current_frag
);
98 frag
.Send_uint8(frag_count
);
99 frag
.Send_uint16(payload_size
);
100 frag
.Send_binary(p
.GetBufferData() + offset
, payload_size
);
102 offset
+= payload_size
;
103 this->SendPacket(frag
, recv
, all
, broadcast
, short_mtu
);
104 frag
.ResetState(PACKET_UDP_EX_MULTI
);
106 assert_msg(current_frag
== frag_count
, "{}, {}", current_frag
, frag_count
);
110 for (auto &s
: this->sockets
) {
111 /* Make a local copy because if we resolve it we cannot
112 * easily unresolve it so we can resolve it later again. */
113 NetworkAddress
send(recv
);
115 /* Not the same type */
116 if (!send
.IsFamily(s
.second
.GetAddress()->ss_family
)) continue;
121 /* Enable broadcast */
122 unsigned long val
= 1;
123 if (setsockopt(s
.first
, SOL_SOCKET
, SO_BROADCAST
, (char *) &val
, sizeof(val
)) < 0) {
124 Debug(net
, 1, "Setting broadcast mode failed: {}", NetworkError::GetLast().AsString());
128 /* Send the buffer */
129 ssize_t res
= p
.TransferOut
<int>(sendto
, s
.first
, 0, (const struct sockaddr
*)send
.GetAddress(), send
.GetAddressLength());
130 Debug(net
, 7, "sendto({})", FormatNetworkAddress(&send
));
132 /* Check for any errors, but ignore it otherwise */
133 if (res
== -1) Debug(net
, 1, "sendto({}) failed with: {}", FormatNetworkAddress(&send
), NetworkError::GetLast().AsString());
140 * Receive a packet at UDP level
142 void NetworkUDPSocketHandler::ReceivePackets()
144 for (auto &s
: this->sockets
) {
145 for (int i
= 0; i
< 1000; i
++) { // Do not infinitely loop when DoSing with UDP
146 struct sockaddr_storage client_addr
;
147 memset(&client_addr
, 0, sizeof(client_addr
));
149 /* The limit is UDP_MTU, but also allocate that much as we need to read the whole packet in one go. */
150 Packet
p(Packet::ReadTag
{}, this, UDP_MTU
, UDP_MTU
);
151 socklen_t client_len
= sizeof(client_addr
);
153 /* Try to receive anything */
154 SetNonBlocking(s
.first
); // Some OSes seem to lose the non-blocking status of the socket
155 ssize_t nbytes
= p
.TransferIn
<int>(recvfrom
, s
.first
, 0, (struct sockaddr
*)&client_addr
, &client_len
);
157 /* Did we get the bytes for the base header of the packet? */
158 if (nbytes
<= 0) break; // No data, i.e. no packet
159 if (nbytes
<= 2) continue; // Invalid data; try next packet
160 #ifdef __EMSCRIPTEN__
161 client_len
= FixAddrLenForEmscripten(client_addr
);
164 NetworkAddress
address(client_addr
, client_len
);
166 /* If the size does not match the packet must be corrupted.
167 * Otherwise it will be marked as corrupted later on. */
168 if (!p
.ParsePacketSize() || static_cast<size_t>(nbytes
) != p
.Size()) {
169 Debug(net
, 1, "received a packet with mismatching size from {}, ({}, {})", FormatNetworkAddress(&address
), (uint
)nbytes
, (uint
)p
.Size());
172 if (!p
.PrepareToRead()) {
173 Debug(net
, 1, "Invalid packet received (too small / decryption error)");
177 /* Handle the packet */
178 this->HandleUDPPacket(p
, address
);
184 * Handle an incoming packets by sending it to the correct function.
185 * @param p the received packet
186 * @param client_addr the sender of the packet
188 void NetworkUDPSocketHandler::HandleUDPPacket(Packet
&p
, NetworkAddress
&client_addr
)
192 /* New packet == new client, which has not quit yet */
195 type
= (PacketUDPType
)p
.Recv_uint8();
197 switch (this->HasClientQuit() ? PACKET_UDP_END
: type
) {
198 case PACKET_UDP_CLIENT_FIND_SERVER
: this->Receive_CLIENT_FIND_SERVER(p
, client_addr
); break;
199 case PACKET_UDP_SERVER_RESPONSE
: this->Receive_SERVER_RESPONSE(p
, client_addr
); break;
201 case PACKET_UDP_EX_MULTI
: this->Receive_EX_MULTI(p
, client_addr
); break;
202 case PACKET_UDP_EX_SERVER_RESPONSE
: this->Receive_EX_SERVER_RESPONSE(p
, client_addr
); break;
205 if (this->HasClientQuit()) {
206 Debug(net
, 0, "[udp] received invalid packet type {} from {}", type
, FormatNetworkAddress(client_addr
));
208 Debug(net
, 0, "[udp] received illegal packet from {}", FormatNetworkAddress(client_addr
));
214 void NetworkUDPSocketHandler::Receive_EX_MULTI(Packet
&p
, NetworkAddress
&client_addr
)
216 uint64_t token
= p
.Recv_uint64();
217 uint8_t index
= p
.Recv_uint8 ();
218 uint8_t total
= p
.Recv_uint8 ();
219 uint16_t payload_size
= p
.Recv_uint16();
221 Debug(net
, 6, "[udp] received multi-part packet from {}: {}, {}/{}, {} bytes",
222 FormatNetworkAddress(client_addr
), token
, index
, total
, payload_size
);
224 if (total
== 0 || index
>= total
) return;
225 if (!p
.CanReadFromPacket(payload_size
)) return;
227 time_t cur_time
= time(nullptr);
229 auto add_to_fragment
= [&](FragmentSet
&fs
) {
230 fs
.fragments
[index
].assign((const char *) p
.GetBufferData() + p
.GetRawPos(), payload_size
);
232 uint total_payload
= 0;
233 for (auto &frag
: fs
.fragments
) {
234 if (!frag
.size()) return;
236 total_payload
+= (uint
)frag
.size();
239 Debug(net
, 6, "[udp] merged multi-part packet from {}: {}, {} bytes",
240 FormatNetworkAddress(client_addr
), token
, total_payload
);
242 Packet
merged(Packet::ReadTag
{}, this, TCP_MTU
, 0);
243 merged
.ReserveBuffer(total_payload
);
244 for (auto &frag
: fs
.fragments
) {
245 merged
.Send_binary((const uint8_t *)frag
.data(), frag
.size());
247 merged
.ParsePacketSize();
248 if (!merged
.PrepareToRead()) return;
250 /* If the size does not match the packet must be corrupted.
251 * Otherwise it will be marked as corrupted later on. */
252 if (total_payload
!= merged
.ReadRawPacketSize()) {
253 Debug(net
, 1, "received an extended packet with mismatching size from {}, ({}, {})",
254 FormatNetworkAddress(client_addr
), (uint
)total_payload
, (uint
)merged
.ReadRawPacketSize());
256 this->HandleUDPPacket(merged
, client_addr
);
259 fs
= this->fragments
.back();
260 this->fragments
.pop_back();
264 while (i
< this->fragments
.size()) {
265 FragmentSet
&fs
= this->fragments
[i
];
266 if (fs
.create_time
< cur_time
- 10) {
267 fs
= this->fragments
.back();
268 this->fragments
.pop_back();
272 if (fs
.token
== token
&& fs
.address
== client_addr
&& fs
.fragments
.size() == total
) {
279 this->fragments
.push_back({ token
, client_addr
, cur_time
, {} });
280 this->fragments
.back().fragments
.resize(total
);
281 add_to_fragment(this->fragments
.back());
285 * Helper for logging receiving invalid packets.
286 * @param type The received packet type.
287 * @param client_addr The address we received the packet from.
289 void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type
, NetworkAddress
&client_addr
)
291 Debug(net
, 0, "[udp] received packet type {} on wrong port from {}", type
, FormatNetworkAddress(client_addr
));
294 void NetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet
&p
, NetworkAddress
&client_addr
) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_FIND_SERVER
, client_addr
); }
295 void NetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet
&p
, NetworkAddress
&client_addr
) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_RESPONSE
, client_addr
); }
296 void NetworkUDPSocketHandler::Receive_EX_SERVER_RESPONSE(Packet
&p
, NetworkAddress
&client_addr
) { this->ReceiveInvalidPacket(PACKET_UDP_EX_SERVER_RESPONSE
, client_addr
); }