]> git.lizzy.rs Git - minetest.git/blob - src/network/socket.cpp
Deal with compiler warnings
[minetest.git] / src / network / socket.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "socket.h"
21
22 #include <cstdio>
23 #include <iostream>
24 #include <cstdlib>
25 #include <cstring>
26 #include <iomanip>
27 #include "util/string.h"
28 #include "util/numeric.h"
29 #include "constants.h"
30 #include "debug.h"
31 #include "log.h"
32
33 #ifdef _WIN32
34 // Without this some of the network functions are not found on mingw
35 #ifndef _WIN32_WINNT
36 #define _WIN32_WINNT 0x0501
37 #endif
38 #include <windows.h>
39 #include <winsock2.h>
40 #include <ws2tcpip.h>
41 #define LAST_SOCKET_ERR() WSAGetLastError()
42 #define SOCKET_ERR_STR(e) itos(e)
43 typedef int socklen_t;
44 #else
45 #include <cerrno>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <fcntl.h>
50 #include <netdb.h>
51 #include <unistd.h>
52 #include <arpa/inet.h>
53 #define LAST_SOCKET_ERR() (errno)
54 #define SOCKET_ERR_STR(e) strerror(e)
55 #endif
56
57 // Set to true to enable verbose debug output
58 bool socket_enable_debug_output = false; // yuck
59
60 static bool g_sockets_initialized = false;
61
62 // Initialize sockets
63 void sockets_init()
64 {
65 #ifdef _WIN32
66         // Windows needs sockets to be initialized before use
67         WSADATA WsaData;
68         if (WSAStartup(MAKEWORD(2, 2), &WsaData) != NO_ERROR)
69                 throw SocketException("WSAStartup failed");
70 #endif
71         g_sockets_initialized = true;
72 }
73
74 void sockets_cleanup()
75 {
76 #ifdef _WIN32
77         // On Windows, cleanup sockets after use
78         WSACleanup();
79 #endif
80 }
81
82 /*
83         UDPSocket
84 */
85
86 UDPSocket::UDPSocket(bool ipv6)
87 {
88         init(ipv6, false);
89 }
90
91 bool UDPSocket::init(bool ipv6, bool noExceptions)
92 {
93         if (!g_sockets_initialized) {
94                 dstream << "Sockets not initialized" << std::endl;
95                 return false;
96         }
97
98         // Use IPv6 if specified
99         m_addr_family = ipv6 ? AF_INET6 : AF_INET;
100         m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP);
101
102         if (socket_enable_debug_output) {
103                 dstream << "UDPSocket(" << (int)m_handle
104                         << ")::UDPSocket(): ipv6 = " << (ipv6 ? "true" : "false")
105                         << std::endl;
106         }
107
108         if (m_handle <= 0) {
109                 if (noExceptions) {
110                         return false;
111                 }
112
113                 throw SocketException(std::string("Failed to create socket: error ") +
114                                       SOCKET_ERR_STR(LAST_SOCKET_ERR()));
115         }
116
117         setTimeoutMs(0);
118
119         if (m_addr_family == AF_INET6) {
120                 // Allow our socket to accept both IPv4 and IPv6 connections
121                 // required on Windows:
122                 // https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
123                 int value = 0;
124                 setsockopt(m_handle, IPPROTO_IPV6, IPV6_V6ONLY,
125                                 reinterpret_cast<char *>(&value), sizeof(value));
126         }
127
128         return true;
129 }
130
131 UDPSocket::~UDPSocket()
132 {
133         if (socket_enable_debug_output) {
134                 dstream << "UDPSocket( " << (int)m_handle << ")::~UDPSocket()"
135                         << std::endl;
136         }
137
138 #ifdef _WIN32
139         closesocket(m_handle);
140 #else
141         close(m_handle);
142 #endif
143 }
144
145 void UDPSocket::Bind(Address addr)
146 {
147         if (socket_enable_debug_output) {
148                 dstream << "UDPSocket(" << (int)m_handle
149                         << ")::Bind(): " << addr.serializeString() << ":"
150                         << addr.getPort() << std::endl;
151         }
152
153         if (addr.getFamily() != m_addr_family) {
154                 const char *errmsg =
155                                 "Socket and bind address families do not match";
156                 errorstream << "Bind failed: " << errmsg << std::endl;
157                 throw SocketException(errmsg);
158         }
159
160         int ret = 0;
161
162         if (m_addr_family == AF_INET6) {
163                 struct sockaddr_in6 address;
164                 memset(&address, 0, sizeof(address));
165
166                 address.sin6_family = AF_INET6;
167                 address.sin6_addr = addr.getAddress6();
168                 address.sin6_port = htons(addr.getPort());
169
170                 ret = bind(m_handle, (const struct sockaddr *) &address,
171                                 sizeof(struct sockaddr_in6));
172         } else {
173                 struct sockaddr_in address;
174                 memset(&address, 0, sizeof(address));
175
176                 address.sin_family = AF_INET;
177                 address.sin_addr = addr.getAddress();
178                 address.sin_port = htons(addr.getPort());
179
180                 ret = bind(m_handle, (const struct sockaddr *) &address,
181                         sizeof(struct sockaddr_in));
182         }
183
184         if (ret < 0) {
185                 dstream << (int)m_handle << ": Bind failed: "
186                         << SOCKET_ERR_STR(LAST_SOCKET_ERR()) << std::endl;
187                 throw SocketException("Failed to bind socket");
188         }
189 }
190
191 void UDPSocket::Send(const Address &destination, const void *data, int size)
192 {
193         bool dumping_packet = false; // for INTERNET_SIMULATOR
194
195         if (INTERNET_SIMULATOR)
196                 dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0;
197
198         if (socket_enable_debug_output) {
199                 // Print packet destination and size
200                 dstream << (int)m_handle << " -> ";
201                 destination.print(&dstream);
202                 dstream << ", size=" << size;
203
204                 // Print packet contents
205                 dstream << ", data=";
206                 for (int i = 0; i < size && i < 20; i++) {
207                         if (i % 2 == 0)
208                                 dstream << " ";
209                         unsigned int a = ((const unsigned char *)data)[i];
210                         dstream << std::hex << std::setw(2) << std::setfill('0') << a;
211                 }
212
213                 if (size > 20)
214                         dstream << "...";
215
216                 if (dumping_packet)
217                         dstream << " (DUMPED BY INTERNET_SIMULATOR)";
218
219                 dstream << std::endl;
220         }
221
222         if (dumping_packet) {
223                 // Lol let's forget it
224                 dstream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet."
225                         << std::endl;
226                 return;
227         }
228
229         if (destination.getFamily() != m_addr_family)
230                 throw SendFailedException("Address family mismatch");
231
232         int sent;
233         if (m_addr_family == AF_INET6) {
234                 struct sockaddr_in6 address = {};
235                 address.sin6_family = AF_INET6;
236                 address.sin6_addr = destination.getAddress6();
237                 address.sin6_port = htons(destination.getPort());
238
239                 sent = sendto(m_handle, (const char *)data, size, 0,
240                                 (struct sockaddr *)&address, sizeof(struct sockaddr_in6));
241         } else {
242                 struct sockaddr_in address = {};
243                 address.sin_family = AF_INET;
244                 address.sin_addr = destination.getAddress();
245                 address.sin_port = htons(destination.getPort());
246
247                 sent = sendto(m_handle, (const char *)data, size, 0,
248                                 (struct sockaddr *)&address, sizeof(struct sockaddr_in));
249         }
250
251         if (sent != size)
252                 throw SendFailedException("Failed to send packet");
253 }
254
255 int UDPSocket::Receive(Address &sender, void *data, int size)
256 {
257         // Return on timeout
258         if (!WaitData(m_timeout_ms))
259                 return -1;
260
261         int received;
262         if (m_addr_family == AF_INET6) {
263                 struct sockaddr_in6 address;
264                 memset(&address, 0, sizeof(address));
265                 socklen_t address_len = sizeof(address);
266
267                 received = recvfrom(m_handle, (char *)data, size, 0,
268                                 (struct sockaddr *)&address, &address_len);
269
270                 if (received < 0)
271                         return -1;
272
273                 u16 address_port = ntohs(address.sin6_port);
274                 const auto *bytes = reinterpret_cast<IPv6AddressBytes*>
275                         (address.sin6_addr.s6_addr);
276                 sender = Address(bytes, address_port);
277         } else {
278                 struct sockaddr_in address;
279                 memset(&address, 0, sizeof(address));
280
281                 socklen_t address_len = sizeof(address);
282
283                 received = recvfrom(m_handle, (char *)data, size, 0,
284                                 (struct sockaddr *)&address, &address_len);
285
286                 if (received < 0)
287                         return -1;
288
289                 u32 address_ip = ntohl(address.sin_addr.s_addr);
290                 u16 address_port = ntohs(address.sin_port);
291
292                 sender = Address(address_ip, address_port);
293         }
294
295         if (socket_enable_debug_output) {
296                 // Print packet sender and size
297                 dstream << (int)m_handle << " <- ";
298                 sender.print(&dstream);
299                 dstream << ", size=" << received;
300
301                 // Print packet contents
302                 dstream << ", data=";
303                 for (int i = 0; i < received && i < 20; i++) {
304                         if (i % 2 == 0)
305                                 dstream << " ";
306                         unsigned int a = ((const unsigned char *)data)[i];
307                         dstream << std::hex << std::setw(2) << std::setfill('0') << a;
308                 }
309                 if (received > 20)
310                         dstream << "...";
311
312                 dstream << std::endl;
313         }
314
315         return received;
316 }
317
318 int UDPSocket::GetHandle()
319 {
320         return m_handle;
321 }
322
323 void UDPSocket::setTimeoutMs(int timeout_ms)
324 {
325         m_timeout_ms = timeout_ms;
326 }
327
328 bool UDPSocket::WaitData(int timeout_ms)
329 {
330         fd_set readset;
331         int result;
332
333         // Initialize the set
334         FD_ZERO(&readset);
335         FD_SET(m_handle, &readset);
336
337         // Initialize time out struct
338         struct timeval tv;
339         tv.tv_sec = 0;
340         tv.tv_usec = timeout_ms * 1000;
341
342         // select()
343         result = select(m_handle + 1, &readset, NULL, NULL, &tv);
344
345         if (result == 0)
346                 return false;
347
348         int e = LAST_SOCKET_ERR();
349 #ifdef _WIN32
350         if (result < 0 && (e == WSAEINTR || e == WSAEBADF)) {
351 #else
352         if (result < 0 && (e == EINTR || e == EBADF)) {
353 #endif
354                 // N.B. select() fails when sockets are destroyed on Connection's dtor
355                 // with EBADF.  Instead of doing tricky synchronization, allow this
356                 // thread to exit but don't throw an exception.
357                 return false;
358         }
359
360         if (result < 0) {
361                 dstream << (int)m_handle << ": Select failed: " << SOCKET_ERR_STR(e)
362                         << std::endl;
363
364                 throw SocketException("Select failed");
365         } else if (!FD_ISSET(m_handle, &readset)) {
366                 // No data
367                 return false;
368         }
369
370         // There is data
371         return true;
372 }