Improve behavior when sending to closed connection
- On Unix, this would previously kill the Yuzu process with SIGPIPE. Send MSG_NOSIGNAL to opt out of this. - Add support for the proper error code in this situation, EPIPE. - Windows has nonstandard behavior in this situation; translate it to the standard behavior. Kind of pointless, but isn't it nice to be correct?
This commit is contained in:
parent
a8c4f01f6c
commit
755bcc459b
|
@ -18,7 +18,9 @@ enum class Errno : u32 {
|
||||||
AGAIN = 11,
|
AGAIN = 11,
|
||||||
INVAL = 22,
|
INVAL = 22,
|
||||||
MFILE = 24,
|
MFILE = 24,
|
||||||
|
PIPE = 32,
|
||||||
MSGSIZE = 90,
|
MSGSIZE = 90,
|
||||||
|
CONNABORTED = 103,
|
||||||
CONNRESET = 104,
|
CONNRESET = 104,
|
||||||
NOTCONN = 107,
|
NOTCONN = 107,
|
||||||
TIMEDOUT = 110,
|
TIMEDOUT = 110,
|
||||||
|
|
|
@ -23,10 +23,14 @@ Errno Translate(Network::Errno value) {
|
||||||
return Errno::INVAL;
|
return Errno::INVAL;
|
||||||
case Network::Errno::MFILE:
|
case Network::Errno::MFILE:
|
||||||
return Errno::MFILE;
|
return Errno::MFILE;
|
||||||
|
case Network::Errno::PIPE:
|
||||||
|
return Errno::PIPE;
|
||||||
case Network::Errno::NOTCONN:
|
case Network::Errno::NOTCONN:
|
||||||
return Errno::NOTCONN;
|
return Errno::NOTCONN;
|
||||||
case Network::Errno::TIMEDOUT:
|
case Network::Errno::TIMEDOUT:
|
||||||
return Errno::TIMEDOUT;
|
return Errno::TIMEDOUT;
|
||||||
|
case Network::Errno::CONNABORTED:
|
||||||
|
return Errno::CONNABORTED;
|
||||||
case Network::Errno::CONNRESET:
|
case Network::Errno::CONNRESET:
|
||||||
return Errno::CONNRESET;
|
return Errno::CONNRESET;
|
||||||
case Network::Errno::INPROGRESS:
|
case Network::Errno::INPROGRESS:
|
||||||
|
|
|
@ -39,6 +39,11 @@ namespace Network {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
enum class CallType {
|
||||||
|
Send,
|
||||||
|
Other,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
using socklen_t = int;
|
using socklen_t = int;
|
||||||
|
@ -96,7 +101,7 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
|
||||||
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
|
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno TranslateNativeError(int e) {
|
Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case 0:
|
case 0:
|
||||||
return Errno::SUCCESS;
|
return Errno::SUCCESS;
|
||||||
|
@ -112,6 +117,14 @@ Errno TranslateNativeError(int e) {
|
||||||
return Errno::AGAIN;
|
return Errno::AGAIN;
|
||||||
case WSAECONNREFUSED:
|
case WSAECONNREFUSED:
|
||||||
return Errno::CONNREFUSED;
|
return Errno::CONNREFUSED;
|
||||||
|
case WSAECONNABORTED:
|
||||||
|
if (call_type == CallType::Send) {
|
||||||
|
// Winsock yields WSAECONNABORTED from `send` in situations where Unix
|
||||||
|
// systems, and actual Switches, yield EPIPE.
|
||||||
|
return Errno::PIPE;
|
||||||
|
} else {
|
||||||
|
return Errno::CONNABORTED;
|
||||||
|
}
|
||||||
case WSAECONNRESET:
|
case WSAECONNRESET:
|
||||||
return Errno::CONNRESET;
|
return Errno::CONNRESET;
|
||||||
case WSAEHOSTUNREACH:
|
case WSAEHOSTUNREACH:
|
||||||
|
@ -198,7 +211,7 @@ bool EnableNonBlock(int fd, bool enable) {
|
||||||
return fcntl(fd, F_SETFL, flags) == 0;
|
return fcntl(fd, F_SETFL, flags) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno TranslateNativeError(int e) {
|
Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case 0:
|
case 0:
|
||||||
return Errno::SUCCESS;
|
return Errno::SUCCESS;
|
||||||
|
@ -208,6 +221,10 @@ Errno TranslateNativeError(int e) {
|
||||||
return Errno::INVAL;
|
return Errno::INVAL;
|
||||||
case EMFILE:
|
case EMFILE:
|
||||||
return Errno::MFILE;
|
return Errno::MFILE;
|
||||||
|
case EPIPE:
|
||||||
|
return Errno::PIPE;
|
||||||
|
case ECONNABORTED:
|
||||||
|
return Errno::CONNABORTED;
|
||||||
case ENOTCONN:
|
case ENOTCONN:
|
||||||
return Errno::NOTCONN;
|
return Errno::NOTCONN;
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
|
@ -236,13 +253,13 @@ Errno TranslateNativeError(int e) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Errno GetAndLogLastError() {
|
Errno GetAndLogLastError(CallType call_type = CallType::Other) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int e = WSAGetLastError();
|
int e = WSAGetLastError();
|
||||||
#else
|
#else
|
||||||
int e = errno;
|
int e = errno;
|
||||||
#endif
|
#endif
|
||||||
const Errno err = TranslateNativeError(e);
|
const Errno err = TranslateNativeError(e, call_type);
|
||||||
if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
|
if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
|
||||||
// These happen during normal operation, so only log them at debug level.
|
// These happen during normal operation, so only log them at debug level.
|
||||||
LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
|
LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
|
||||||
|
@ -731,13 +748,17 @@ std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
|
||||||
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
|
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
|
||||||
|
int native_flags = 0;
|
||||||
|
#if YUZU_UNIX
|
||||||
|
native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
|
||||||
|
#endif
|
||||||
const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
|
const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
|
||||||
static_cast<int>(message.size()), 0);
|
static_cast<int>(message.size()), native_flags);
|
||||||
if (result != SOCKET_ERROR) {
|
if (result != SOCKET_ERROR) {
|
||||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {-1, GetAndLogLastError()};
|
return {-1, GetAndLogLastError(CallType::Send)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
|
||||||
|
@ -759,7 +780,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
|
||||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {-1, GetAndLogLastError()};
|
return {-1, GetAndLogLastError(CallType::Send)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno Socket::Close() {
|
Errno Socket::Close() {
|
||||||
|
|
|
@ -33,10 +33,12 @@ enum class Errno {
|
||||||
BADF,
|
BADF,
|
||||||
INVAL,
|
INVAL,
|
||||||
MFILE,
|
MFILE,
|
||||||
|
PIPE,
|
||||||
NOTCONN,
|
NOTCONN,
|
||||||
AGAIN,
|
AGAIN,
|
||||||
CONNREFUSED,
|
CONNREFUSED,
|
||||||
CONNRESET,
|
CONNRESET,
|
||||||
|
CONNABORTED,
|
||||||
HOSTUNREACH,
|
HOSTUNREACH,
|
||||||
NETDOWN,
|
NETDOWN,
|
||||||
NETUNREACH,
|
NETUNREACH,
|
||||||
|
|
Reference in New Issue