Merge branch 'master' into jgrpp-beta
# Conflicts: # src/engine_base.h # src/gfxinit.cpp # src/graph_gui.cpp # src/lang/brazilian_portuguese.txt # src/lang/dutch.txt # src/lang/french.txt # src/lang/korean.txt # src/lang/norwegian_bokmal.txt # src/lang/portuguese.txt # src/lang/russian.txt # src/lang/spanish.txt # src/lang/spanish_MX.txt # src/network/core/address.cpp # src/network/core/game_info.h # src/network/core/os_abstraction.h # src/network/core/udp.cpp # src/network/network_client.cpp # src/network/network_client.h # src/network/network_internal.h # src/newgrf_engine.cpp # src/settings_gui.cpp # src/station_cmd.cpp # src/string_func.h # src/town_gui.cpp # src/video/video_driver.cpp # src/widget_type.h
This commit is contained in:
@@ -8,6 +8,7 @@ add_files(
|
||||
game_info.h
|
||||
host.cpp
|
||||
host.h
|
||||
os_abstraction.cpp
|
||||
os_abstraction.h
|
||||
packet.cpp
|
||||
packet.h
|
||||
|
||||
@@ -94,6 +94,18 @@ void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool wit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address as a string, e.g. 127.0.0.1:12345.
|
||||
* @param with_family whether to add the family (e.g. IPvX).
|
||||
* @return the address
|
||||
*/
|
||||
std::string NetworkAddress::GetAddressAsString(bool with_family)
|
||||
{
|
||||
char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
|
||||
this->GetAddressAsString(buf, lastof(buf), with_family);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address as a string, e.g. 127.0.0.1:12345.
|
||||
* @param with_family whether to add the family (e.g. IPvX).
|
||||
@@ -311,12 +323,11 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
|
||||
{
|
||||
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
|
||||
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
|
||||
char address[NETWORK_HOSTNAME_LENGTH + 6 + 7];
|
||||
NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address));
|
||||
std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
|
||||
|
||||
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkGetLastErrorString());
|
||||
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
@@ -325,8 +336,8 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
|
||||
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type);
|
||||
|
||||
int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen);
|
||||
if (err != 0 && NetworkGetLastError() != EINPROGRESS) {
|
||||
DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString());
|
||||
if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) {
|
||||
DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkError::GetLast().AsString());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
@@ -342,28 +353,28 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
|
||||
tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS;
|
||||
int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv);
|
||||
if (n < 0) {
|
||||
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkError::GetLast().AsString());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* If no fd is selected, the timeout has been reached. */
|
||||
if (n == 0) {
|
||||
DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address);
|
||||
DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Retrieve last error, if any, on the socket. */
|
||||
err = GetSocketError(sock);
|
||||
if (err != 0) {
|
||||
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetErrorString(err));
|
||||
NetworkError socket_error = GetSocketError(sock);
|
||||
if (socket_error.HasError()) {
|
||||
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), socket_error.AsString());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Connection succeeded. */
|
||||
DEBUG(net, 1, "[%s] connected to %s", type, address);
|
||||
DEBUG(net, 1, "[%s] connected to %s", type, address.c_str());
|
||||
|
||||
return sock;
|
||||
}
|
||||
@@ -388,48 +399,47 @@ static SOCKET ListenLoopProc(addrinfo *runp)
|
||||
{
|
||||
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
|
||||
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
|
||||
char address[NETWORK_HOSTNAME_LENGTH + 6 + 7];
|
||||
NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address));
|
||||
std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
|
||||
|
||||
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
|
||||
DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address);
|
||||
DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address.c_str());
|
||||
}
|
||||
|
||||
int on = 1;
|
||||
/* The (const char*) cast is needed for windows!! */
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) {
|
||||
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
|
||||
}
|
||||
|
||||
#ifndef __OS2__
|
||||
if (runp->ai_family == AF_INET6 &&
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) {
|
||||
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkError::GetLast().AsString());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
|
||||
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
|
||||
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkGetLastErrorString());
|
||||
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Connection succeeded */
|
||||
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address);
|
||||
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address.c_str());
|
||||
|
||||
DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address);
|
||||
DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address.c_str());
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ struct NetworkAddressDumper {
|
||||
const char *GetAddressAsString(NetworkAddress *addr, bool with_family = true);
|
||||
|
||||
private:
|
||||
/* 6 = for the : and 5 for the decimal port number */
|
||||
char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
|
||||
/* 7 extra are for with_family, which adds " (IPvX)". */
|
||||
char buf[NETWORK_HOSTNAME_PORT_LENGTH + 7];
|
||||
};
|
||||
|
||||
#endif /* NETWORK_CORE_ADDRESS_H */
|
||||
|
||||
@@ -57,6 +57,7 @@ static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What vers
|
||||
static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
|
||||
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'
|
||||
static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
|
||||
static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536)
|
||||
static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
|
||||
static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0'
|
||||
static const uint NETWORK_LONG_REVISION_LENGTH = 64; ///< The maximum length of the revision, in bytes including '\0'
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "../../debug.h"
|
||||
#include "os_abstraction.h"
|
||||
#include "packet.h"
|
||||
#include "../../string_func.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
@@ -48,20 +47,3 @@ void NetworkCoreShutdown()
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
/**
|
||||
* Return the string representation of the given error from the OS's network functions.
|
||||
* @param error The error number (from \c NetworkGetLastError()).
|
||||
* @return The error message, potentially an empty string but never \c nullptr.
|
||||
*/
|
||||
const char *NetworkGetErrorString(int error)
|
||||
{
|
||||
static char buffer[512];
|
||||
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
|
||||
seprintf(buffer, lastof(buffer), "Unknown error %d", error);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
@@ -74,7 +74,6 @@ struct NetworkGameInfo : NetworkServerGameInfo {
|
||||
uint32 map_width; ///< Map width
|
||||
uint32 map_height; ///< Map height
|
||||
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
|
||||
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any)
|
||||
char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated)
|
||||
char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
|
||||
bool dedicated; ///< Is this a dedicated server?
|
||||
|
||||
217
src/network/core/os_abstraction.cpp
Normal file
217
src/network/core/os_abstraction.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to set the socket into non-blocking mode.
|
||||
* @param d The socket to set the non-blocking more for.
|
||||
* @return True if setting the non-blocking mode succeeded, otherwise false.
|
||||
*/
|
||||
bool SetNonBlocking(SOCKET d)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
u_long nonblocking = 1;
|
||||
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
|
||||
#elif defined __EMSCRIPTEN__
|
||||
return true;
|
||||
#else
|
||||
int nonblocking = 1;
|
||||
return ioctl(d, FIONBIO, &nonblocking) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket into blocking mode.
|
||||
* @param d The socket to set the blocking more for.
|
||||
* @return True if setting the blocking mode succeeded, otherwise false.
|
||||
*/
|
||||
bool SetBlocking(SOCKET d)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
u_long nonblocking = 0;
|
||||
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
|
||||
#elif defined __EMSCRIPTEN__
|
||||
return true;
|
||||
#else
|
||||
int nonblocking = 0;
|
||||
return ioctl(d, FIONBIO, &nonblocking) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket to not delay sending.
|
||||
* @param d The socket to disable the delaying for.
|
||||
* @return True if disabling the delaying succeeded, otherwise false.
|
||||
*/
|
||||
bool SetNoDelay(SOCKET d)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
return true;
|
||||
#else
|
||||
int flags = 1;
|
||||
/* The (const char*) cast is needed for windows */
|
||||
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to shutdown the socket in one or both directions.
|
||||
* @param d The socket to disable the delaying for.
|
||||
* @param read Whether to shutdown the read direction.
|
||||
* @param write Whether to shutdown the write direction.
|
||||
* @param linger_timeout The socket linger timeout.
|
||||
* @return True if successful
|
||||
*/
|
||||
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
|
||||
{
|
||||
if (!read && !write) return true;
|
||||
#ifdef _WIN32
|
||||
LINGER ln = { 1U, (uint16) linger_timeout };
|
||||
#else
|
||||
struct linger ln = { 1, (int) linger_timeout };
|
||||
#endif
|
||||
|
||||
setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
|
||||
|
||||
int how = SD_BOTH;
|
||||
if (!read) how = SD_SEND;
|
||||
if (!write) how = SD_RECEIVE;
|
||||
return shutdown(d, how) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error from a socket, if any.
|
||||
* @param d The socket to get the error from.
|
||||
* @return The errno on the socket.
|
||||
*/
|
||||
NetworkError GetSocketError(SOCKET d)
|
||||
{
|
||||
int err;
|
||||
socklen_t len = sizeof(err);
|
||||
getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
|
||||
|
||||
return NetworkError(err);
|
||||
}
|
||||
@@ -14,6 +14,26 @@
|
||||
#ifndef NETWORK_CORE_OS_ABSTRACTION_H
|
||||
#define NETWORK_CORE_OS_ABSTRACTION_H
|
||||
|
||||
/**
|
||||
* Abstraction of a network error where all implementation details of the
|
||||
* error codes are encapsulated in this class and the abstraction layer.
|
||||
*/
|
||||
class NetworkError {
|
||||
private:
|
||||
int error; ///< The underlying error number from errno or WSAGetLastError.
|
||||
mutable std::string message; ///< The string representation of the error (set on first call to #AsString).
|
||||
public:
|
||||
NetworkError(int error);
|
||||
|
||||
bool HasError() const;
|
||||
bool WouldBlock() const;
|
||||
bool IsConnectionReset() const;
|
||||
bool IsConnectInProgress() const;
|
||||
const char *AsString() const;
|
||||
|
||||
static NetworkError GetLast();
|
||||
};
|
||||
|
||||
/* Include standard stuff per OS */
|
||||
|
||||
/* Windows stuff */
|
||||
@@ -23,21 +43,6 @@
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
/**
|
||||
* Get the last error code from any of the OS's network functions.
|
||||
* What it returns and when it is reset, is implementation defined.
|
||||
* @return The last error code.
|
||||
*/
|
||||
#define NetworkGetLastError() WSAGetLastError()
|
||||
#undef EWOULDBLOCK
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#undef ECONNRESET
|
||||
#define ECONNRESET WSAECONNRESET
|
||||
#undef EINPROGRESS
|
||||
#define EINPROGRESS WSAEWOULDBLOCK
|
||||
|
||||
const char *NetworkGetErrorString(int error);
|
||||
|
||||
/* Windows has some different names for some types */
|
||||
typedef unsigned long in_addr_t;
|
||||
|
||||
@@ -74,10 +79,7 @@ typedef unsigned long in_addr_t;
|
||||
# endif
|
||||
# define SOCKET int
|
||||
# define INVALID_SOCKET -1
|
||||
# define ioctlsocket ioctl
|
||||
# define closesocket close
|
||||
# define NetworkGetLastError() (errno)
|
||||
# define NetworkGetErrorString(error) (strerror(error))
|
||||
# define SD_RECEIVE SHUT_RD
|
||||
# define SD_SEND SHUT_WR
|
||||
# define SD_BOTH SHUT_RDWR
|
||||
@@ -128,10 +130,7 @@ typedef unsigned long in_addr_t;
|
||||
#if defined(__OS2__)
|
||||
# define SOCKET int
|
||||
# define INVALID_SOCKET -1
|
||||
# define ioctlsocket ioctl
|
||||
# define closesocket close
|
||||
# define NetworkGetLastError() (sock_errno())
|
||||
# define NetworkGetErrorString(error) (strerror(error))
|
||||
# define SD_RECEIVE SHUT_RD
|
||||
# define SD_SEND SHUT_WR
|
||||
# define SD_BOTH SHUT_RDWR
|
||||
@@ -206,105 +205,11 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return the string representation of the last error from the OS's network functions.
|
||||
* @return The error message, potentially an empty string but never \c nullptr.
|
||||
*/
|
||||
static inline const char *NetworkGetLastErrorString()
|
||||
{
|
||||
return NetworkGetErrorString(NetworkGetLastError());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket into non-blocking mode.
|
||||
* @param d The socket to set the non-blocking more for.
|
||||
* @return True if setting the non-blocking mode succeeded, otherwise false.
|
||||
*/
|
||||
static inline bool SetNonBlocking(SOCKET d)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
return true;
|
||||
#else
|
||||
# ifdef _WIN32
|
||||
u_long nonblocking = 1;
|
||||
# else
|
||||
int nonblocking = 1;
|
||||
# endif
|
||||
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket into blocking mode.
|
||||
* @param d The socket to set the blocking more for.
|
||||
* @return True if setting the blocking mode succeeded, otherwise false.
|
||||
*/
|
||||
static inline bool SetBlocking(SOCKET d)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long nonblocking = 0;
|
||||
#else
|
||||
int nonblocking = 0;
|
||||
#endif
|
||||
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket to not delay sending.
|
||||
* @param d The socket to disable the delaying for.
|
||||
* @return True if disabling the delaying succeeded, otherwise false.
|
||||
*/
|
||||
static inline bool SetNoDelay(SOCKET d)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
return true;
|
||||
#else
|
||||
/* XXX should this be done at all? */
|
||||
int b = 1;
|
||||
/* The (const char*) cast is needed for windows */
|
||||
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to shutdown the socket in one or both directions.
|
||||
* @param d The socket to disable the delaying for.
|
||||
* @param read Whether to shutdown the read direction.
|
||||
* @param write Whether to shutdown the write direction.
|
||||
* @param linger_timeout The socket linger timeout.
|
||||
* @return True if successful
|
||||
*/
|
||||
static inline bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
|
||||
{
|
||||
if (!read && !write) return true;
|
||||
#ifdef _WIN32
|
||||
LINGER ln = { 1U, (uint16) linger_timeout };
|
||||
#else
|
||||
struct linger ln = { 1, (int) linger_timeout };
|
||||
#endif
|
||||
|
||||
setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
|
||||
|
||||
int how = SD_BOTH;
|
||||
if (!read) how = SD_SEND;
|
||||
if (!write) how = SD_RECEIVE;
|
||||
return shutdown(d, how) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error from a socket, if any.
|
||||
* @param d The socket to get the error from.
|
||||
* @return The errno on the socket.
|
||||
*/
|
||||
static inline int GetSocketError(SOCKET d)
|
||||
{
|
||||
int err;
|
||||
socklen_t len = sizeof(err);
|
||||
getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
|
||||
|
||||
return err;
|
||||
}
|
||||
bool SetNonBlocking(SOCKET d);
|
||||
bool SetBlocking(SOCKET d);
|
||||
bool SetNoDelay(SOCKET d);
|
||||
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout);
|
||||
NetworkError GetSocketError(SOCKET d);
|
||||
|
||||
/* Make sure these structures have the size we expect them to be */
|
||||
static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes.
|
||||
|
||||
@@ -114,11 +114,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
|
||||
Packet *p = this->packet_queue.front().get();
|
||||
res = p->TransferOut<int>(send, this->sock, 0);
|
||||
if (res == -1) {
|
||||
int err = NetworkGetLastError();
|
||||
if (err != EWOULDBLOCK) {
|
||||
NetworkError err = NetworkError::GetLast();
|
||||
if (!err.WouldBlock()) {
|
||||
/* Something went wrong.. close client! */
|
||||
if (!closing_down) {
|
||||
DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err));
|
||||
DEBUG(net, 0, "send failed with error %s", err.AsString());
|
||||
this->CloseConnection();
|
||||
}
|
||||
return SPS_CLOSED;
|
||||
@@ -165,10 +165,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
|
||||
while (p->RemainingBytesToTransfer() != 0) {
|
||||
res = p->TransferIn<int>(recv, this->sock, 0);
|
||||
if (res == -1) {
|
||||
int err = NetworkGetLastError();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (ECONNRESET is connection reset by peer) */
|
||||
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
|
||||
NetworkError err = NetworkError::GetLast();
|
||||
if (!err.WouldBlock()) {
|
||||
/* Something went wrong... */
|
||||
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
|
||||
this->CloseConnection();
|
||||
return nullptr;
|
||||
}
|
||||
@@ -194,10 +194,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
|
||||
while (p->RemainingBytesToTransfer() != 0) {
|
||||
res = p->TransferIn<int>(recv, this->sock, 0);
|
||||
if (res == -1) {
|
||||
int err = NetworkGetLastError();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (ECONNRESET is connection reset by peer) */
|
||||
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
|
||||
NetworkError err = NetworkError::GetLast();
|
||||
if (!err.WouldBlock()) {
|
||||
/* Something went wrong... */
|
||||
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
|
||||
this->CloseConnection();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ static const char* _packet_game_type_names[] {
|
||||
"SERVER_ERROR",
|
||||
"CLIENT_COMPANY_INFO",
|
||||
"SERVER_COMPANY_INFO",
|
||||
"CLIENT_GAME_INFO",
|
||||
"SERVER_GAME_INFO",
|
||||
"CLIENT_GAME_INFO",
|
||||
"SERVER_GAME_INFO_EXTENDED",
|
||||
"SERVER_CHECK_NEWGRFS",
|
||||
"CLIENT_NEWGRFS_CHECKED",
|
||||
|
||||
@@ -44,8 +44,8 @@ enum PacketGameType {
|
||||
PACKET_SERVER_COMPANY_INFO, ///< Information about a single company.
|
||||
|
||||
/* Packets used to get the game info. */
|
||||
PACKET_CLIENT_GAME_INFO, ///< Request information about the server.
|
||||
PACKET_SERVER_GAME_INFO, ///< Information about the server.
|
||||
PACKET_CLIENT_GAME_INFO, ///< Request information about the server.
|
||||
|
||||
/*
|
||||
* Packets after here assume that the client
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../rev.h"
|
||||
#include "../network_func.h"
|
||||
#include "../network_internal.h"
|
||||
#include "game_info.h"
|
||||
|
||||
#include "tcp_http.h"
|
||||
@@ -203,11 +203,7 @@ int NetworkHTTPSocketHandler::HandleHeader()
|
||||
|
||||
*url = '\0';
|
||||
|
||||
/* Fetch the hostname, and possible port number. */
|
||||
const char *port = nullptr;
|
||||
ParseConnectionString(&port, hname);
|
||||
|
||||
NetworkAddress address(hname, port == nullptr ? 80 : atoi(port));
|
||||
NetworkAddress address = ParseConnectionString(hname, 80);
|
||||
|
||||
/* Restore the URL. */
|
||||
*url = '/';
|
||||
@@ -229,10 +225,10 @@ int NetworkHTTPSocketHandler::Receive()
|
||||
for (;;) {
|
||||
ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0);
|
||||
if (res == -1) {
|
||||
int err = NetworkGetLastError();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (ECONNRESET is connection reset by peer) */
|
||||
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
|
||||
NetworkError err = NetworkError::GetLast();
|
||||
if (!err.WouldBlock()) {
|
||||
/* Something went wrong... */
|
||||
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
|
||||
return -1;
|
||||
}
|
||||
/* Connection would block, so stop for now */
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str());
|
||||
|
||||
if (p.TransferOut<int>(send, s, 0) < 0) {
|
||||
DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString());
|
||||
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
|
||||
}
|
||||
closesocket(s);
|
||||
break;
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
p.PrepareToSend();
|
||||
|
||||
if (p.TransferOut<int>(send, s, 0) < 0) {
|
||||
DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString());
|
||||
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
|
||||
}
|
||||
closesocket(s);
|
||||
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
|
||||
if (sockets.size() == 0) {
|
||||
DEBUG(net, 0, "[server] could not start network: could not create listening socket");
|
||||
NetworkError(STR_NETWORK_ERROR_SERVER_START);
|
||||
ShowNetworkError(STR_NETWORK_ERROR_SERVER_START);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
|
||||
/* Enable broadcast */
|
||||
unsigned long val = 1;
|
||||
if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
|
||||
DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkGetLastErrorString());
|
||||
DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
|
||||
DEBUG(net, 7, "[udp] sendto(%s)", NetworkAddressDumper().GetAddressAsString(&send));
|
||||
|
||||
/* Check for any errors, but ignore it otherwise */
|
||||
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkGetLastErrorString());
|
||||
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkError::GetLast().AsString());
|
||||
|
||||
if (!all) break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user