-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Implement NamedPipe*Stream on Unix domain sockets #6833
Changes from all commits
4c0556a
8c0e687
75a1014
fcc5cc9
c39ac9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,7 @@ | |
| #endif | ||
| #include <unistd.h> | ||
| #include <vector> | ||
| #include <pwd.h> | ||
|
|
||
| #if HAVE_KQUEUE | ||
| #if KEVENT_HAS_VOID_UDATA | ||
|
|
@@ -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) | ||
| { | ||
| 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) : | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Net.Sockets; | ||
| using System.Reflection; | ||
| using System.Runtime.InteropServices; | ||
| using System.Security; | ||
|
|
||
|
|
@@ -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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Anonymous pipes.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
resultvalue is never freed. Is that intentional. Could not find a reference to whether this needs to be freed or not in the docs.There was a problem hiding this comment.
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.