1 /*
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/>.
6  */
7 
8 /**
9  * @file os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff.
10  *
11  * The general idea is to have simple abstracting functions for things that
12  * require different implementations for different environments.
13  * In here the functions, and their documentation, are defined only once
14  * and the implementation contains the #ifdefs to change the implementation.
15  * Since Windows is usually different that is usually the first case, after
16  * that the behaviour is usually Unix/BSD-like with occasional variation.
17  */
18 
19 #include "stdafx.h"
20 #include "os_abstraction.h"
21 #include "../../string_func.h"
22 #include <mutex>
23 
24 #include "../../safeguards.h"
25 
26 /**
27  * Construct the network error with the given error code.
28  * @param error The error code.
29  */
NetworkError(int error)30 NetworkError::NetworkError(int error) : error(error)
31 {
32 }
33 
34 /**
35  * Check whether this error describes that the operation would block.
36  * @return True iff the operation would block.
37  */
WouldBlock() const38 bool NetworkError::WouldBlock() const
39 {
40 #if defined(_WIN32)
41 	return this->error == WSAEWOULDBLOCK;
42 #else
43 	/* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not
44 	 * and the POSIX.1 specification states that either should be checked. */
45 	return this->error == EWOULDBLOCK || this->error == EAGAIN;
46 #endif
47 }
48 
49 /**
50  * Check whether this error describes a connection reset.
51  * @return True iff the connection is reset.
52  */
IsConnectionReset() const53 bool NetworkError::IsConnectionReset() const
54 {
55 #if defined(_WIN32)
56 	return this->error == WSAECONNRESET;
57 #else
58 	return this->error == ECONNRESET;
59 #endif
60 }
61 
62 /**
63  * Check whether this error describes a connect is in progress.
64  * @return True iff the connect is already in progress.
65  */
IsConnectInProgress() const66 bool NetworkError::IsConnectInProgress() const
67 {
68 #if defined(_WIN32)
69 	return this->error == WSAEWOULDBLOCK;
70 #else
71 	return this->error == EINPROGRESS;
72 #endif
73 }
74 
75 /**
76  * Get the string representation of the error message.
77  * @return The string representation that will get overwritten by next calls.
78  */
AsString() const79 const std::string &NetworkError::AsString() const
80 {
81 	if (this->message.empty()) {
82 #if defined(_WIN32)
83 		char buffer[512];
84 		if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error,
85 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
86 			seprintf(buffer, lastof(buffer), "Unknown error %d", this->error);
87 		}
88 		this->message.assign(buffer);
89 #else
90 		/* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however
91 		 * the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable.
92 		 * The problem with the non-POSIX variant is that it does not necessarily fill the buffer with
93 		 * the error message but can also return a pointer to a static bit of memory, whereas the POSIX
94 		 * variant always fills the buffer. This makes the behaviour too erratic to work with. */
95 		static std::mutex mutex;
96 		std::lock_guard<std::mutex> guard(mutex);
97 		this->message.assign(strerror(this->error));
98 #endif
99 	}
100 	return this->message;
101 }
102 
103 /**
104  * Check whether an error was actually set.
105  * @return True iff an error was set.
106  */
HasError() const107 bool NetworkError::HasError() const
108 {
109 	return this->error != 0;
110 }
111 
112 /**
113  * Get the last network error.
114  * @return The network error.
115  */
GetLast()116 /* static */ NetworkError NetworkError::GetLast()
117 {
118 #if defined(_WIN32)
119 	return NetworkError(WSAGetLastError());
120 #elif defined(__OS2__)
121 	return NetworkError(sock_errno());
122 #else
123 	return NetworkError(errno);
124 #endif
125 }
126 
127 
128 /**
129  * Try to set the socket into non-blocking mode.
130  * @param d The socket to set the non-blocking more for.
131  * @return True if setting the non-blocking mode succeeded, otherwise false.
132  */
SetNonBlocking(SOCKET d)133 bool SetNonBlocking(SOCKET d)
134 {
135 #if defined(_WIN32)
136 	u_long nonblocking = 1;
137 	return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
138 #elif defined __EMSCRIPTEN__
139 	return true;
140 #else
141 	int nonblocking = 1;
142 	return ioctl(d, FIONBIO, &nonblocking) == 0;
143 #endif
144 }
145 
146 /**
147  * Try to set the socket to not delay sending.
148  * @param d The socket to disable the delaying for.
149  * @return True if disabling the delaying succeeded, otherwise false.
150  */
SetNoDelay(SOCKET d)151 bool SetNoDelay(SOCKET d)
152 {
153 #ifdef __EMSCRIPTEN__
154 	return true;
155 #else
156 	int flags = 1;
157 	/* The (const char*) cast is needed for windows */
158 	return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0;
159 #endif
160 }
161 
162 /**
163  * Try to set the socket to reuse ports.
164  * @param d The socket to reuse ports on.
165  * @return True if disabling the delaying succeeded, otherwise false.
166  */
SetReusePort(SOCKET d)167 bool SetReusePort(SOCKET d)
168 {
169 #ifdef _WIN32
170 	/* Windows has no SO_REUSEPORT, but for our usecases SO_REUSEADDR does the same job. */
171 	int reuse_port = 1;
172 	return setsockopt(d, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_port, sizeof(reuse_port)) == 0;
173 #else
174 	int reuse_port = 1;
175 	return setsockopt(d, SOL_SOCKET, SO_REUSEPORT, &reuse_port, sizeof(reuse_port)) == 0;
176 #endif
177 }
178 
179 /**
180  * Get the error from a socket, if any.
181  * @param d The socket to get the error from.
182  * @return The errno on the socket.
183  */
GetSocketError(SOCKET d)184 NetworkError GetSocketError(SOCKET d)
185 {
186 	int err;
187 	socklen_t len = sizeof(err);
188 	getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
189 
190 	return NetworkError(err);
191 }
192