summaryrefslogtreecommitdiff
path: root/src/network/core/os_abstraction.cpp
blob: 75f2224eb0dfae4fe68de4b519e00b8b9faaa177 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * This file is part of OpenTTD.
 * 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.
 * 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.
 * 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/>.
 */

/**
 * @file os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff.
 *
 * The general idea is to have simple abstracting functions for things that
 * require different implementations for different environments.
 * In here the functions, and their documentation, are defined only once
 * and the implementation contains the #ifdefs to change the implementation.
 * Since Windows is usually different that is usually the first case, after
 * that the behaviour is usually Unix/BSD-like with occasional variation.
 */

#include "stdafx.h"
#include "os_abstraction.h"
#include "../../string_func.h"
#include <mutex>

#include "../../safeguards.h"

/**
 * Construct the network error with the given error code.
 * @param error The error code.
 */
NetworkError::NetworkError(int error) : error(error)
{
}

/**
 * Check whether this error describes that the operation would block.
 * @return True iff the operation would block.
 */
bool NetworkError::WouldBlock() const
{
#if defined(_WIN32)
	return this->error == WSAEWOULDBLOCK;
#else
	/* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not
	 * and the POSIX.1 specification states that either should be checked. */
	return this->error == EWOULDBLOCK || this->error == EAGAIN;
#endif
}

/**
 * Check whether this error describes a connection reset.
 * @return True iff the connection is reset.
 */
bool NetworkError::IsConnectionReset() const
{
#if defined(_WIN32)
	return this->error == WSAECONNRESET;
#else
	return this->error == ECONNRESET;
#endif
}

/**
 * Check whether this error describes a connect is in progress.
 * @return True iff the connect is already in progress.
 */
bool NetworkError::IsConnectInProgress() const
{
#if defined(_WIN32)
	return this->error == WSAEWOULDBLOCK;
#else
	return this->error == EINPROGRESS;
#endif
}

/**
 * Get the string representation of the error message.
 * @return The string representation that will get overwritten by next calls.
 */
const char *NetworkError::AsString() const
{
	if (this->message.empty()) {
#if defined(_WIN32)
		char buffer[512];
		if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
			seprintf(buffer, lastof(buffer), "Unknown error %d", this->error);
		}
		this->message.assign(buffer);
#else
		/* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however
		 * the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable.
		 * The problem with the non-POSIX variant is that it does not necessarily fill the buffer with
		 * the error message but can also return a pointer to a static bit of memory, whereas the POSIX
		 * variant always fills the buffer. This makes the behaviour too erratic to work with. */
		static std::mutex mutex;
		std::lock_guard<std::mutex> guard(mutex);
		this->message.assign(strerror(this->error));
#endif
	}
	return this->message.c_str();
}

/**
 * Check whether an error was actually set.
 * @return True iff an error was set.
 */
bool NetworkError::HasError() const
{
	return this->error != 0;
}

/**
 * Get the last network error.
 * @return The network error.
 */
/* static */ NetworkError NetworkError::GetLast()
{
#if defined(_WIN32)
	return NetworkError(WSAGetLastError());
#elif defined(__OS2__)
	return NetworkError(sock_errno());
#else
	return NetworkError(errno);
#endif
}