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