< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.UnixDomainSocket.Security.UnixDomainSocketInterop
Assembly: CoreWCF.UnixDomainSocket
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.UnixDomainSocket/src/CoreWCF/Security/UnixDomainSocketInterop.cs
Line coverage
52%
Covered lines: 9
Uncovered lines: 8
Coverable lines: 17
Total lines: 245
Line coverage: 52.9%
Branch coverage
50%
Covered branches: 2
Total branches: 4
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
TryGetCredentials(...)50%4452.94%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.UnixDomainSocket/src/CoreWCF/Security/UnixDomainSocketInterop.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Buffers;
 6using System.Collections.Generic;
 7using System.Net.Sockets;
 8using System.Runtime.InteropServices;
 9
 10namespace CoreWCF.UnixDomainSocket.Security
 11{
 12    internal static class UnixDomainSocketInterop
 13    {
 14
 15        internal static bool TryGetCredentials(this Socket socket, out uint processId, out uint userId, out uint groupId
 16        {
 17            try
 18            {
 119                if (OperatingSystem.IsWindows())
 020                    throw new PlatformNotSupportedException();
 21
 122                Span<uint> ucred = stackalloc uint[3];
 23                int bytesWritten;
 124                if (OperatingSystem.IsMacOS())
 25                {
 026                    bytesWritten = socket.GetRawSocketOption(0, 2, MemoryMarshal.AsBytes(ucred)); // get processid
 027                    processId = ucred[0];
 028                    userId = 0;
 029                    groupId = 0;
 030                    return true;
 31                }
 32                else
 33                {
 34                    const int SOL_SOCKET = 1;
 35                    const int SO_PEERCRED = 17;
 136                    bytesWritten = socket.GetRawSocketOption(SOL_SOCKET, SO_PEERCRED, MemoryMarshal.AsBytes(ucred));
 37                }
 138                processId = ucred[0];
 139                userId = ucred[1];
 140                groupId = ucred[2];
 141                return bytesWritten == (ucred.Length * sizeof(uint));
 42            }
 043            catch(Exception ex)
 44            {
 045                throw;
 46            }
 147        }
 48
 49    }
 50
 51    internal static class NativeSysCall
 52    {
 53        private const string LIBC = "libc";
 54        private const int InitialBufferSize = 1024;
 55        // Hard upper bound to avoid unbounded growth on a misbehaving NSS implementation.
 56        private const int MaxBufferSize = 16 * 1024 * 1024;
 57        // POSIX errno value for "buffer too small". Both Linux and macOS use 34.
 58        private const int ERANGE = 34;
 59
 60        [DllImport(LIBC, SetLastError = false)]
 61        private static extern unsafe int getpwuid_r(uint uid, PasswdRaw* pwd, byte* buf, nuint buflen, PasswdRaw** resul
 62
 63        [DllImport(LIBC, SetLastError = false)]
 64        private static extern unsafe int getgrgid_r(uint gid, GroupRaw* grp, byte* buf, nuint buflen, GroupRaw** result)
 65
 66        [StructLayout(LayoutKind.Sequential)]
 67        private unsafe struct PasswdRaw
 68        {
 69            public byte* pw_name;
 70            public byte* pw_passwd;
 71            public uint  pw_uid;
 72            public uint  pw_gid;
 73            public byte* pw_gecos;
 74            public byte* pw_dir;
 75            public byte* pw_shell;
 76        }
 77
 78        [StructLayout(LayoutKind.Sequential)]
 79        private unsafe struct GroupRaw
 80        {
 81            public byte*  gr_name;
 82            public byte*  gr_passwd;
 83            public uint   gr_gid;
 84            // gr_mem is char**: a NULL-terminated array of char*.
 85            public byte** gr_mem;
 86        }
 87
 88        internal static UserInfo GetUserInfo(uint uid)
 89        {
 90            int bufSize = InitialBufferSize;
 91            while (true)
 92            {
 93                byte[] buf = ArrayPool<byte>.Shared.Rent(bufSize);
 94                try
 95                {
 96                    int err = ReadUserInfo(uid, buf, out UserInfo info, out bool found);
 97                    if (err == 0)
 98                    {
 99                        return found ? info : null;
 100                    }
 101                    if (err == ERANGE && bufSize < MaxBufferSize)
 102                    {
 103                        bufSize = Math.Min(bufSize * 2, MaxBufferSize);
 104                        continue;
 105                    }
 106                    return null;
 107                }
 108                finally
 109                {
 110                    ArrayPool<byte>.Shared.Return(buf);
 111                }
 112            }
 113        }
 114
 115        internal static GroupInfo GetGroupInfo(uint gid)
 116        {
 117            int bufSize = InitialBufferSize;
 118            while (true)
 119            {
 120                byte[] buf = ArrayPool<byte>.Shared.Rent(bufSize);
 121                try
 122                {
 123                    int err = ReadGroupInfo(gid, buf, out GroupInfo info, out bool found);
 124                    if (err == 0)
 125                    {
 126                        return found ? info : null;
 127                    }
 128                    if (err == ERANGE && bufSize < MaxBufferSize)
 129                    {
 130                        bufSize = Math.Min(bufSize * 2, MaxBufferSize);
 131                        continue;
 132                    }
 133                    return null;
 134                }
 135                finally
 136                {
 137                    ArrayPool<byte>.Shared.Return(buf);
 138                }
 139            }
 140        }
 141
 142        private static unsafe int ReadUserInfo(uint uid, byte[] buf, out UserInfo info, out bool found)
 143        {
 144            info = null;
 145            found = false;
 146            PasswdRaw pwd;
 147            PasswdRaw* result;
 148            int err;
 149            fixed (byte* pBuf = buf)
 150            {
 151                err = getpwuid_r(uid, &pwd, pBuf, (nuint)buf.Length, &result);
 152                if (err != 0 || result == null)
 153                {
 154                    return err;
 155                }
 156                string name = Marshal.PtrToStringAnsi((IntPtr)pwd.pw_name);
 157                if (name == null)
 158                {
 159                    return 0;
 160                }
 161                string directory = Marshal.PtrToStringAnsi((IntPtr)pwd.pw_dir);
 162                string shell = Marshal.PtrToStringAnsi((IntPtr)pwd.pw_shell);
 163                info = new UserInfo(name, pwd.pw_uid, pwd.pw_gid, directory, shell);
 164                found = true;
 165                return 0;
 166            }
 167        }
 168
 169        private static unsafe int ReadGroupInfo(uint gid, byte[] buf, out GroupInfo info, out bool found)
 170        {
 171            info = null;
 172            found = false;
 173            GroupRaw grp;
 174            GroupRaw* result;
 175            int err;
 176            fixed (byte* pBuf = buf)
 177            {
 178                err = getgrgid_r(gid, &grp, pBuf, (nuint)buf.Length, &result);
 179                if (err != 0 || result == null)
 180                {
 181                    return err;
 182                }
 183                string name = Marshal.PtrToStringAnsi((IntPtr)grp.gr_name);
 184                if (name == null)
 185                {
 186                    return 0;
 187                }
 188                string[] members = MaterializeMembers(grp.gr_mem);
 189                info = new GroupInfo(name, grp.gr_gid, members);
 190                found = true;
 191                return 0;
 192            }
 193        }
 194
 195        private static unsafe string[] MaterializeMembers(byte** gr_mem)
 196        {
 197            if (gr_mem == null)
 198            {
 199                return Array.Empty<string>();
 200            }
 201            var list = new List<string>();
 202            for (int i = 0; gr_mem[i] != null; i++)
 203            {
 204                string member = Marshal.PtrToStringAnsi((IntPtr)gr_mem[i]);
 205                if (member != null)
 206                {
 207                    list.Add(member);
 208                }
 209            }
 210            return list.ToArray();
 211        }
 212    }
 213
 214    internal sealed class GroupInfo
 215    {
 216        public string Name { get; }
 217        public uint Id { get; }
 218        public string[] Members { get; }
 219
 220        internal GroupInfo(string name, uint id, string[] members)
 221        {
 222            Name = name;
 223            Id = id;
 224            Members = members ?? Array.Empty<string>();
 225        }
 226    }
 227
 228    internal class UserInfo
 229    {
 230        public string Name { get; }
 231        public uint Uid { get; }
 232        public uint Gid { get; }
 233        public string Directory { get; }
 234        public string Shell { get; }
 235
 236        internal UserInfo(string name, uint uid, uint gid, string directory, string shell)
 237        {
 238            Name = name;
 239            Uid = uid;
 240            Gid = gid;
 241            Directory = directory;
 242            Shell = shell;
 243        }
 244    }
 245}