< Summary

Information
Class: Renci.SshNet.NetConf.NetConfSession
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Netconf\NetConfSession.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 128
Coverable lines: 128
Total lines: 205
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 28
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%10%
get_ServerCapabilities()100%10%
get_ClientCapabilities()100%10%
SendReceiveRpc(...)0%80%
OnChannelOpen()100%10%
OnDataReceived(...)0%120%
Dispose(...)0%60%

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Netconf\NetConfSession.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.Text;
 4using System.Text.RegularExpressions;
 5using System.Threading;
 6using System.Xml;
 7
 8using Renci.SshNet.Common;
 9
 10namespace Renci.SshNet.NetConf
 11{
 12    internal sealed class NetConfSession : SubsystemSession, INetConfSession
 13    {
 14        private const string Prompt = "]]>]]>";
 15
 016        private readonly StringBuilder _data = new StringBuilder();
 17        private bool _usingFramingProtocol;
 018        private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(initialState: false);
 019        private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(initialState: false);
 020        private StringBuilder _rpcReply = new StringBuilder();
 21        private int _messageId;
 22
 23        /// <summary>
 24        /// Gets NetConf server capabilities.
 25        /// </summary>
 026        public XmlDocument ServerCapabilities { get; private set; }
 27
 28        /// <summary>
 29        /// Gets NetConf client capabilities.
 30        /// </summary>
 031        public XmlDocument ClientCapabilities { get; private set; }
 32
 33        /// <summary>
 34        /// Initializes a new instance of the <see cref="NetConfSession"/> class.
 35        /// </summary>
 36        /// <param name="session">The session.</param>
 37        /// <param name="operationTimeout">The number of milliseconds to wait for an operation to complete, or -1 to wai
 38        public NetConfSession(ISession session, int operationTimeout)
 039            : base(session, "netconf", operationTimeout)
 040        {
 041            ClientCapabilities = new XmlDocument();
 042            ClientCapabilities.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
 043                                                "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
 044                                                    "<capabilities>" +
 045                                                        "<capability>" +
 046                                                            "urn:ietf:params:netconf:base:1.0" +
 047                                                        "</capability>" +
 048                                                    "</capabilities>" +
 049                                                "</hello>");
 050        }
 51
 52        public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling)
 053        {
 054            _ = _data.Clear();
 55
 056            XmlNamespaceManager nsMgr = null;
 057            if (automaticMessageIdHandling)
 058            {
 059                _messageId++;
 060                nsMgr = new XmlNamespaceManager(rpc.NameTable);
 061                nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
 062                rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value = _messageId.ToString(CultureInfo.InvariantCult
 063            }
 64
 065            _rpcReply = new StringBuilder();
 066            _ = _rpcReplyReceived.Reset();
 067            var reply = new XmlDocument();
 068            if (_usingFramingProtocol)
 069            {
 070                var command = new StringBuilder(rpc.InnerXml.Length + 10);
 071                _ = command.AppendFormat(CultureInfo.InvariantCulture, "\n#{0}\n", rpc.InnerXml.Length);
 072                _ = command.Append(rpc.InnerXml);
 073                _ = command.Append("\n##\n");
 074                SendData(Encoding.UTF8.GetBytes(command.ToString()));
 75
 076                WaitOnHandle(_rpcReplyReceived, OperationTimeout);
 077                reply.LoadXml(_rpcReply.ToString());
 078            }
 79            else
 080            {
 081                SendData(Encoding.UTF8.GetBytes(rpc.InnerXml + Prompt));
 082                WaitOnHandle(_rpcReplyReceived, OperationTimeout);
 083                reply.LoadXml(_rpcReply.ToString());
 084            }
 85
 086            if (automaticMessageIdHandling)
 087            {
 088                var replyId = rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value;
 089                if (replyId != _messageId.ToString(CultureInfo.InvariantCulture))
 090                {
 091                    throw new NetConfServerException("The rpc message id does not match the rpc-reply message id.");
 92                }
 093            }
 94
 095            return reply;
 096        }
 97
 98        protected override void OnChannelOpen()
 099        {
 0100            _ = _data.Clear();
 101
 0102            var message = string.Concat(ClientCapabilities.InnerXml, Prompt);
 103
 0104            SendData(Encoding.UTF8.GetBytes(message));
 105
 0106            WaitOnHandle(_serverCapabilitiesConfirmed, OperationTimeout);
 0107        }
 108
 109        protected override void OnDataReceived(byte[] data)
 0110        {
 0111            var chunk = Encoding.UTF8.GetString(data);
 112
 0113            if (ServerCapabilities is null)
 0114            {
 0115                _ = _data.Append(chunk);
 116
 0117                if (!chunk.Contains(Prompt))
 0118                {
 0119                    return;
 120                }
 121
 122                try
 0123                {
 0124                    chunk = _data.ToString();
 0125                    _ = _data.Clear();
 126
 0127                    ServerCapabilities = new XmlDocument();
 0128                    ServerCapabilities.LoadXml(chunk.Replace(Prompt, string.Empty));
 0129                }
 0130                catch (XmlException e)
 0131                {
 0132                    throw new NetConfServerException("Server capabilities received are not well formed XML", e);
 133                }
 134
 0135                var nsMgr = new XmlNamespaceManager(ServerCapabilities.NameTable);
 0136                nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
 137
 0138                _usingFramingProtocol = ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[tex
 139
 0140                _ = _serverCapabilitiesConfirmed.Set();
 0141            }
 0142            else if (_usingFramingProtocol)
 0143            {
 0144                var position = 0;
 145
 146                for (; ; )
 0147                {
 0148                    var match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
 0149                    if (!match.Success)
 0150                    {
 0151                        break;
 152                    }
 153
 0154                    var fractionLength = Convert.ToInt32(match.Groups["length"].Value, CultureInfo.InvariantCulture);
 0155                    _ = _rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength);
 0156                    position += match.Index + match.Length + fractionLength;
 0157                }
 158
 159#if NET7_0_OR_GREATER
 0160                if (Regex.IsMatch(chunk.AsSpan(position), @"\n##\n"))
 161#else
 0162                if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
 163#endif // NET7_0_OR_GREATER
 0164                {
 0165                    _ = _rpcReplyReceived.Set();
 0166                }
 0167            }
 168            else
 0169            {
 0170                _ = _data.Append(chunk);
 171
 0172                if (!chunk.Contains(Prompt))
 0173                {
 0174                    return;
 175                }
 176
 0177                chunk = _data.ToString();
 0178                _ = _data.Clear();
 179
 0180                _ = _rpcReply.Append(chunk.Replace(Prompt, string.Empty));
 0181                _ = _rpcReplyReceived.Set();
 0182            }
 0183        }
 184
 185        protected override void Dispose(bool disposing)
 0186        {
 0187            base.Dispose(disposing);
 188
 0189            if (disposing)
 0190            {
 0191                if (_serverCapabilitiesConfirmed != null)
 0192                {
 0193                    _serverCapabilitiesConfirmed.Dispose();
 0194                    _serverCapabilitiesConfirmed = null;
 0195                }
 196
 0197                if (_rpcReplyReceived != null)
 0198                {
 0199                    _rpcReplyReceived.Dispose();
 0200                    _rpcReplyReceived = null;
 0201                }
 0202            }
 0203        }
 204    }
 205}