using System.Net.Sockets; using System.Text; namespace InnovEnergy.Lib.Protocols.DBus.Transport; public class AuthenticationMethod { public static AuthenticationMethod Anonymous = new AuthenticationMethod(AuthenticateAnonymous); public static AuthenticationMethod External(UInt16 userId) => new AuthenticationMethod(s => AuthenticateExternal(s, userId)); public static AuthenticationMethod External() => new AuthenticationMethod(s => AuthenticateExternal(s, Env.UserId)); public static AuthenticationMethod ExternalAsRoot() => new AuthenticationMethod(s => AuthenticateExternal(s, 0)); // TODO: other auth modes public Func Authenticate { get; } private AuthenticationMethod(Func authenticate) { Authenticate = authenticate; } private static Guid AuthenticateExternal(Socket socket, UInt16 userId) { var user = String .Concat(Encoding.ASCII.GetBytes(userId.ToString()) .Select(c => c.ToString("X2"))); return AuthenticateAsClient(socket, $"AUTH EXTERNAL {user}"); } private static Guid AuthenticateAnonymous(Socket s) { return AuthenticateAsClient(s, "AUTH ANONYMOUS"); } private static Guid AuthenticateAsClient(Socket socket, String authCommand) { socket.Send(new Byte[] { 0 }, SocketFlags.None); SendLine(authCommand); var reply = ReceiveLine(); if (reply is null) throw new Exception("Connection failure. Connection closed."); var args = reply.Trim().Split(' '); if (args[0] != "OK") throw new Exception("Authentication failure"); SendLine("BEGIN"); return args[1] != String.Empty ? Guid.ParseExact(args[1], "N") : Guid.Empty; String? ReceiveLine() { using var stream = new NetworkStream(socket); using var reader = new StreamReader(stream, Encoding.ASCII); return reader.ReadLine(); } void SendLine(String line) { using var stream = new NetworkStream(socket); using var writer = new StreamWriter(stream, Encoding.ASCII) { NewLine = "\r\n" }; writer.WriteLine(line); } } }