Skip to content

Commit

Permalink
Unix: improve SocketError.SocketError Exception Message (dotnet#38073)
Browse files Browse the repository at this point in the history
* Unix: improve SocketError.SocketError Exception Message

This introduces a fake errno for SocketError.SocketError, which
maps to an error message of "Unknown socket error".

* PR feedback

Co-authored-by: Stephen Toub <stoub@microsoft.com>

Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
tmds and stephentoub authored Jun 19, 2020
1 parent 21e1c86 commit 1869ee6
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/libraries/Common/src/Interop/Unix/Interop.Errors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ internal enum Error

// Custom Error codes to track errors beyond kernel interface.
EHOSTNOTFOUND = 0x20001, // Name lookup failed
ESOCKETERROR = 0x20002, // Unspecified socket error

// POSIX permits these to have the same value and we make them always equal so
// that we do not introduce a dependency on distinguishing between them that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static SocketErrorPal()
#endif

private const int NativeErrorToSocketErrorCount = 42;
private const int SocketErrorToNativeErrorCount = 40;
private const int SocketErrorToNativeErrorCount = 41;

// No Interop.Errors are included for the following SocketErrors, as there's no good mapping:
// - SocketError.NoRecovery
Expand Down Expand Up @@ -126,6 +126,7 @@ static SocketErrorPal()
{ SocketError.TooManyOpenSockets, Interop.Error.ENFILE }, // could also have been EMFILE
{ SocketError.TryAgain, Interop.Error.EAGAIN }, // not a perfect mapping, but better than nothing
{ SocketError.WouldBlock, Interop.Error.EAGAIN },
{ SocketError.SocketError, Interop.Error.ESOCKETERROR },
};

internal static SocketError GetSocketErrorForNativeError(Interop.Error errno)
Expand Down
45 changes: 36 additions & 9 deletions src/libraries/Native/Unix/Common/pal_error_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ typedef enum

// Error codes to track errors beyond kernel.
Error_EHOSTNOTFOUND = 0x20001, // Name lookup failed.
Error_ESOCKETERROR = 0x20002, // Unidentified socket error.

// POSIX permits these to have the same value and we make them
// always equal so that we cannot introduce a dependency on
Expand All @@ -137,6 +138,17 @@ typedef enum
Error_ENONSTANDARD = 0x1FFFF,
} Error;

/*
Some pal errors don't have a corresponding errno value.
We define values for these errors.
We want these values to be distinct from real errno values.
We base of the Error enum values which are chosen to be out of the
typical errno range, and make them negative because POSIX
requires errno values to be positive.
*/
#define EHOSTNOTFOUND (-Error_EHOSTNOTFOUND)
#define ESOCKETERROR (-Error_ESOCKETERROR)

inline static int32_t ConvertErrorPlatformToPal(int32_t platformErrno)
{
switch (platformErrno)
Expand Down Expand Up @@ -500,7 +512,10 @@ inline static int32_t ConvertErrorPalToPlatform(int32_t error)
case Error_ENODATA:
return ENODATA;
case Error_EHOSTNOTFOUND:
return -(Error_EHOSTNOTFOUND);
return EHOSTNOTFOUND;
case Error_ESOCKETERROR:
return ESOCKETERROR;

case Error_ENONSTANDARD:
break; // fall through to assert
}
Expand All @@ -518,16 +533,19 @@ inline static int32_t ConvertErrorPalToPlatform(int32_t error)
return -1;
}

static int32_t ConvertErrorPalToGai(int32_t error)
static bool TryConvertErrorToGai(int32_t error, int32_t* gaiError)
{
assert(gaiError != NULL);

switch (error)
{
case -(Error_EHOSTNOTFOUND):
return EAI_NONAME;
case EHOSTNOTFOUND:
*gaiError = EAI_NONAME;
return true;
default:
*gaiError = error;
return false;
}
// Fall-through for unknown codes. gai_strerror() will handle that.

return error;
}


Expand All @@ -543,8 +561,17 @@ inline static const char* StrErrorR(int32_t platformErrno, char* buffer, int32_t
if (platformErrno < 0)
{
// Not a system error
SafeStringCopy(buffer, (size_t)bufferSize, gai_strerror(ConvertErrorPalToGai(platformErrno)));
return buffer;
int32_t gaiError;
if (TryConvertErrorToGai(platformErrno, &gaiError))
{
SafeStringCopy(buffer, (size_t)bufferSize, gai_strerror(gaiError));
return buffer;
}
else if (platformErrno == ESOCKETERROR)
{
SafeStringCopy(buffer, (size_t)bufferSize, "Unknown socket error");
return buffer;
}
}

// Note that we must use strerror_r because plain strerror is not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,11 @@ private SocketException(Interop.ErrorInfo error) : this(SocketErrorPal.GetSocket
private static int GetNativeErrorForSocketError(SocketError error)
{
int nativeErr = (int)error;
if (error != SocketError.SocketError)
{
Interop.Error interopErr;

// If an interop error was not found, then don't invoke Info().RawErrno as that will fail with assert.
if (SocketErrorPal.TryGetNativeErrorForSocketError(error, out interopErr))
{
nativeErr = interopErr.Info().RawErrno;
}
// If an interop error was not found, then don't invoke Info().RawErrno as that will fail with assert.
if (SocketErrorPal.TryGetNativeErrorForSocketError(error, out Interop.Error interopErr))
{
nativeErr = interopErr.Info().RawErrno;
}

return nativeErr;
Expand Down

0 comments on commit 1869ee6

Please sign in to comment.