Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal static partial class Interop
{
internal static partial class Sys
{
internal static class Fcntl
internal static partial class Fcntl
{
internal static readonly bool CanGetSetPipeSz = (FcntlCanGetSetPipeSz() != 0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ internal static partial class Interop
{
internal static partial class Sys
{
internal static class Fcntl
internal static partial class Fcntl
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FcntlSetCloseOnExec", SetLastError=true)]
internal static extern int SetCloseOnExec(SafeFileHandle fd);
internal static extern int SetCloseOnExec(SafeHandle fd);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkFifo", SetLastError = true)]
internal static extern int MkFifo(string path, int mode);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPeerUserName", SetLastError = true)]
internal unsafe static extern string GetPeerUserName(SafeHandle socket);
}
}
1 change: 1 addition & 0 deletions src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ internal static class FileTypes
internal const int S_IFDIR = 0x4000;
internal const int S_IFREG = 0x8000;
internal const int S_IFLNK = 0xA000;
internal const int S_IFSOCK = 0xC000;
}

[Flags]
Expand Down
3 changes: 2 additions & 1 deletion src/Common/src/System/Net/Sockets/SocketErrorPal.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static SocketErrorPal()
}
#endif

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

// No Interop.Errors are included for the following SocketErrors, as there's no good mapping:
Expand Down Expand Up @@ -62,6 +62,7 @@ static SocketErrorPal()
{ Interop.Error.ENFILE, SocketError.TooManyOpenSockets },
{ Interop.Error.ENOBUFS, SocketError.NoBufferSpaceAvailable },
{ Interop.Error.ENODATA, SocketError.NoData },
{ Interop.Error.ENOENT, SocketError.AddressNotAvailable },
{ Interop.Error.ENOPROTOOPT, SocketError.ProtocolOption },
{ Interop.Error.ENOTCONN, SocketError.NotConnected },
{ Interop.Error.ENOTSOCK, SocketError.NotSocket },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace System.Net.Sockets
{
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary>
public sealed class UnixDomainSocketEndPoint : EndPoint
internal sealed class UnixDomainSocketEndPoint : EndPoint
{
private const int MaxPathLength = 92; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html
private const int PathOffset = 2; // = offsetof(struct sockaddr_un, sun_path). It's the same on Linux and OSX
Expand Down
1 change: 1 addition & 0 deletions src/Native/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#cmakedefine01 HAVE_FCOPYFILE
#cmakedefine01 HAVE_GETHOSTBYNAME_R
#cmakedefine01 HAVE_GETHOSTBYADDR_R
#cmakedefine01 HAVE_GETPEEREID
#cmakedefine01 HAVE_SUPPORT_FOR_DUAL_MODE_IPV4_PACKET_INFO
#cmakedefine01 HAVE_THREAD_SAFE_GETHOSTBYNAME_AND_GETHOSTBYADDR
#cmakedefine01 HAVE_TCGETATTR
Expand Down
8 changes: 1 addition & 7 deletions src/Native/System.Native/pal_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static_assert(PAL_S_IFCHR == S_IFCHR, "");
static_assert(PAL_S_IFDIR == S_IFDIR, "");
static_assert(PAL_S_IFREG == S_IFREG, "");
static_assert(PAL_S_IFLNK == S_IFLNK, "");
static_assert(PAL_S_IFSOCK == S_IFSOCK, "");

// Validate that our enum for inode types is the same as what is
// declared by the dirent.h header on the local system.
Expand Down Expand Up @@ -502,13 +503,6 @@ extern "C" int32_t SystemNative_FChMod(intptr_t fd, int32_t mode)
return result;
}

extern "C" int32_t SystemNative_MkFifo(const char* path, int32_t mode)
{
int32_t result;
while (CheckInterrupted(result = mkfifo(path, static_cast<mode_t>(mode))));
return result;
}

extern "C" int32_t SystemNative_FSync(intptr_t fd)
{
int32_t result;
Expand Down
8 changes: 1 addition & 7 deletions src/Native/System.Native/pal_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum
PAL_S_IFDIR = 0x4000, // Directory
PAL_S_IFREG = 0x8000, // Regular file
PAL_S_IFLNK = 0xA000, // Symbolic link
PAL_S_IFSOCK = 0xC000, // Socket
};

/**
Expand Down Expand Up @@ -438,13 +439,6 @@ extern "C" int32_t SystemNative_ChMod(const char* path, int32_t mode);
*/
extern "C" int32_t SystemNative_FChMod(intptr_t fd, int32_t mode);

/**
* Create a FIFO (named pipe). Implemented as a shim to mkfifo(3).
*
* Returns 0 for success, -1 for failure. Sets errno for failure.
*/
extern "C" int32_t SystemNative_MkFifo(const char* path, int32_t mode);

/**
* Flushes all modified data and attribtues of the specified File Descriptor to the storage medium.
*
Expand Down
61 changes: 61 additions & 0 deletions src/Native/System.Native/pal_networking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#endif
#include <unistd.h>
#include <vector>
#include <pwd.h>

#if HAVE_KQUEUE
#if KEVENT_HAS_VOID_UDATA
Expand Down Expand Up @@ -2527,3 +2528,63 @@ extern "C" int32_t SystemNative_PlatformSupportsDualModeIPv4PacketInfo()
return 0;
#endif
}

static char* GetNameFromUid(uid_t uid)
{
size_t bufferLength = 512;
while (true)
{
char *buffer = reinterpret_cast<char*>(malloc(bufferLength));
if (buffer == nullptr)
return nullptr;

struct passwd pw;
struct passwd* result;
if (getpwuid_r(uid, &pw, buffer, bufferLength, &result) == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result value is never freed. Is that intentional. Could not find a reference to whether this needs to be freed or not in the docs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit of a strangely designed function. result is set to &pw on success or to NULL if the entry couldn't be found. Either way, nothing to be freed.

{
if (result == nullptr)
{
errno = ENOENT;
free(buffer);
return nullptr;
}
else
{
char* name = strdup(pw.pw_name);
free(buffer);
return name;
}
}

free(buffer);
if (errno == ERANGE)
{
bufferLength *= 2;
}
else
{
return nullptr;
}
}
}

extern "C" char* SystemNative_GetPeerUserName(intptr_t socket)
{
int fd = ToFileDescriptor(socket);
#ifdef SO_PEERCRED
struct ucred creds;
socklen_t len = sizeof(creds);
return getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &len) == 0 ?
GetNameFromUid(creds.uid) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a side-effect of the condition here feels odd to me. I think I'd rather this just be an if/else statement, so the execution order is more apparent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really? I rather like this construction 😄 If it really bothers you, I can change it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't insist. :)

nullptr;
#elif HAVE_GETPEEREID
uid_t euid, egid;
return getpeereid(fd, &euid, &egid) == 0 ?
GetNameFromUid(euid) :
nullptr;
#else
(void)fd;
errno = ENOTSUP;
return nullptr;
#endif
}
2 changes: 2 additions & 0 deletions src/Native/System.Native/pal_networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,3 +413,5 @@ extern "C" Error SystemNative_TryChangeSocketEventRegistration(
extern "C" Error SystemNative_WaitForSocketEvents(int32_t port, SocketEvent* buffer, int32_t* count);

extern "C" int32_t SystemNative_PlatformSupportsDualModeIPv4PacketInfo();

extern "C" char* SystemNative_GetPeerUserName(intptr_t socket);
4 changes: 4 additions & 0 deletions src/Native/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ check_include_files(
linux/rtnetlink.h
HAVE_LINUX_RTNETLINK_H)

check_function_exists(
getpeereid
HAVE_GETPEEREID)

# getdomainname on OSX takes an 'int' instead of a 'size_t'
# check if compiling with 'size_t' would cause a warning
set (PREVIOUS_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;

Expand All @@ -13,34 +15,69 @@ public sealed partial class SafePipeHandle : SafeHandle
{
private const int DefaultInvalidHandle = -1;

/// <summary>Opens the specified file with the requested flags and mode.</summary>
/// <param name="path">The path to the file.</param>
/// <param name="flags">The flags with which to open the file.</param>
/// <param name="mode">The mode for opening the file.</param>
/// <returns>A SafeFileHandle for the opened file.</returns>
internal static SafePipeHandle Open(string path, Interop.Sys.OpenFlags flags, int mode)
// For anonymous pipes, SafePipeHandle.handle is the file descriptor of the pipe, and the
// _named* fields remain null. For named pipes, SafePipeHandle.handle is a copy of the file descriptor
// extracted from the Socket's SafeHandle, and the _named* fields are the socket and its safe handle.
// This allows operations related to file descriptors to be performed directly on the SafePipeHandle,
// and operations that should go through the Socket to be done via _namedPipeSocket. We keep the
// Socket's SafeHandle alive as long as this SafeHandle is alive.

private Socket _namedPipeSocket;
private SafeHandle _namedPipeSocketHandle;
private static PropertyInfo s_safeHandleProperty;

internal SafePipeHandle(Socket namedPipeSocket) : base((IntPtr)DefaultInvalidHandle, ownsHandle: true)
{
// Ideally this would be a constrained execution region, but we don't have access to PrepareConstrainedRegions.
SafePipeHandle handle = Interop.CheckIo(Interop.Sys.OpenPipe(path, flags, mode));
Debug.Assert(namedPipeSocket != null);
_namedPipeSocket = namedPipeSocket;

// TODO: Issue https://github.com/dotnet/corefx/issues/6807
// This is unfortunately the only way of getting at the Socket's file descriptor right now, until #6807 is implemented.
PropertyInfo safeHandleProperty = s_safeHandleProperty ?? (s_safeHandleProperty = typeof(Socket).GetTypeInfo().GetDeclaredProperty("SafeHandle"));
Debug.Assert(safeHandleProperty != null, "Socket.SafeHandle could not be found.");
_namedPipeSocketHandle = (SafeHandle)safeHandleProperty?.GetValue(namedPipeSocket, null);

bool ignored = false;
_namedPipeSocketHandle.DangerousAddRef(ref ignored);
SetHandle(_namedPipeSocketHandle.DangerousGetHandle());
}

Debug.Assert(!handle.IsInvalid);
internal Socket NamedPipeSocket => _namedPipeSocket;
internal SafeHandle NamedPipeSocketHandle => _namedPipeSocketHandle;

return handle;
protected override void Dispose(bool disposing)
{
base.Dispose(disposing); // must be called before trying to Dispose the socket
if (disposing && _namedPipeSocket != null)
{
_namedPipeSocket.Dispose();
_namedPipeSocket = null;
}
}

protected override bool ReleaseHandle()
{
// Close the handle. Although close is documented to potentially fail with EINTR, we never want
// to retry, as the descriptor could actually have been closed, been subsequently reassigned, and
// be in use elsewhere in the process. Instead, we simply check whether the call was successful.
Debug.Assert(!this.IsInvalid);
return Interop.Sys.Close(handle) == 0;
Debug.Assert(!IsInvalid);

// Clean up resources for named handles
if (_namedPipeSocketHandle != null)
{
SetHandle(DefaultInvalidHandle);
_namedPipeSocketHandle.DangerousRelease();
_namedPipeSocketHandle = null;
return true;
}

// Clean up resources for anonymous handles
return (long)handle >= 0 ?
Interop.Sys.Close(handle) == 0 :
true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would we end up with a handle that needs to be closed here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anonymous pipes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh. Might be good to have a comment to that effect.

}

public override bool IsInvalid
{
[SecurityCritical]
get { return (long)handle < 0; }
get { return (long)handle < 0 && _namedPipeSocket == null; }
}
}
}
16 changes: 13 additions & 3 deletions src/System.IO.Pipes/src/System.IO.Pipes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<ProjectJson>win/project.json</ProjectJson>
<ProjectLockJson>win/project.lock.json</ProjectLockJson>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetsUnix)' == 'true' ">
<ProjectJson>unix/project.json</ProjectJson>
<ProjectLockJson>unix/project.lock.json</ProjectLockJson>
</PropertyGroup>
<!-- Help VS understand available configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Unix_Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Unix_Release|AnyCPU'" />
Expand Down Expand Up @@ -170,18 +174,21 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Fcntl.Pipe.cs">
<Link>Common\Interop\Unix\Interop.Fcntl.Pipe.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Fcntl.SetCloseOnExec.cs">
<Link>Common\Interop\Unix\Interop.Fcntl.SetCloseOnExec.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.FLock.cs">
<Link>Common\Interop\Unix\Interop.FLock.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetHostName.cs">
<Link>Common\Interop\Unix\Interop.GetHostName.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetPeerUserName.cs">
<Link>Common\Interop\Unix\Interop.GetPeerUserName.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.MkDir.cs">
<Link>Common\Interop\Unix\Interop.MkDir.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.MkFifo.cs">
<Link>Common\Interop\Unix\Interop.MkFifo.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Open.cs">
<Link>Common\Interop\Unix\Interop.Open.cs</Link>
</Compile>
Expand Down Expand Up @@ -221,6 +228,9 @@
<Compile Include="$(CommonPath)\System\Threading\Tasks\ForceAsyncAwaiter.cs">
<Link>Common\System\Threading\Tasks\ForceAsyncAwaiter.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\Sockets\UnixDomainSocketEndPoint.cs">
<Link>Common\System\Net\UnixDomainSocketEndPoint.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' == 'net46'">
<TargetingPackReference Include="mscorlib" />
Expand Down
Loading