You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
7.4 KiB
C#
197 lines
7.4 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using Renci.SshNet.Common;
|
|
using System.Xml;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Renci.SshNet.NetConf
|
|
{
|
|
internal class NetConfSession : SubsystemSession, INetConfSession
|
|
{
|
|
private const string Prompt = "]]>]]>";
|
|
|
|
private readonly StringBuilder _data = new StringBuilder();
|
|
private bool _usingFramingProtocol;
|
|
private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(false);
|
|
private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(false);
|
|
private StringBuilder _rpcReply = new StringBuilder();
|
|
private int _messageId;
|
|
|
|
/// <summary>
|
|
/// Gets NetConf server capabilities.
|
|
/// </summary>
|
|
public XmlDocument ServerCapabilities { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets NetConf client capabilities.
|
|
/// </summary>
|
|
public XmlDocument ClientCapabilities { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="NetConfSession"/> class.
|
|
/// </summary>
|
|
/// <param name="session">The session.</param>
|
|
/// <param name="operationTimeout">The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely.</param>
|
|
public NetConfSession(ISession session, int operationTimeout)
|
|
: base(session, "netconf", operationTimeout, Encoding.UTF8)
|
|
{
|
|
ClientCapabilities = new XmlDocument();
|
|
ClientCapabilities.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
|
"<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
|
|
"<capabilities>" +
|
|
"<capability>" +
|
|
"urn:ietf:params:netconf:base:1.0" +
|
|
"</capability>" +
|
|
"</capabilities>" +
|
|
"</hello>");
|
|
|
|
}
|
|
|
|
public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling)
|
|
{
|
|
_data.Clear();
|
|
|
|
XmlNamespaceManager nsMgr = null;
|
|
if (automaticMessageIdHandling)
|
|
{
|
|
_messageId++;
|
|
nsMgr = new XmlNamespaceManager(rpc.NameTable);
|
|
nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
|
|
rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value = _messageId.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
_rpcReply = new StringBuilder();
|
|
_rpcReplyReceived.Reset();
|
|
var reply = new XmlDocument();
|
|
if (_usingFramingProtocol)
|
|
{
|
|
var command = new StringBuilder(rpc.InnerXml.Length + 10);
|
|
command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length);
|
|
command.Append(rpc.InnerXml);
|
|
command.Append("\n##\n");
|
|
SendData(Encoding.UTF8.GetBytes(command.ToString()));
|
|
|
|
WaitOnHandle(_rpcReplyReceived, OperationTimeout);
|
|
reply.LoadXml(_rpcReply.ToString());
|
|
}
|
|
else
|
|
{
|
|
SendData(Encoding.UTF8.GetBytes(rpc.InnerXml + Prompt));
|
|
WaitOnHandle(_rpcReplyReceived, OperationTimeout);
|
|
reply.LoadXml(_rpcReply.ToString());
|
|
}
|
|
if (automaticMessageIdHandling)
|
|
{
|
|
var replyId = rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value;
|
|
if (replyId != _messageId.ToString(CultureInfo.InvariantCulture))
|
|
{
|
|
throw new NetConfServerException("The rpc message id does not match the rpc-reply message id.");
|
|
}
|
|
}
|
|
return reply;
|
|
}
|
|
|
|
protected override void OnChannelOpen()
|
|
{
|
|
_data.Clear();
|
|
|
|
var message = string.Format("{0}{1}", ClientCapabilities.InnerXml, Prompt);
|
|
|
|
SendData(Encoding.UTF8.GetBytes(message));
|
|
|
|
WaitOnHandle(_serverCapabilitiesConfirmed, OperationTimeout);
|
|
}
|
|
|
|
protected override void OnDataReceived(byte[] data)
|
|
{
|
|
var chunk = Encoding.UTF8.GetString(data);
|
|
|
|
if (ServerCapabilities == null) // This must be server capabilities, old protocol
|
|
{
|
|
_data.Append(chunk);
|
|
|
|
if (!chunk.Contains(Prompt))
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
chunk = _data.ToString();
|
|
_data.Clear();
|
|
|
|
ServerCapabilities = new XmlDocument();
|
|
ServerCapabilities.LoadXml(chunk.Replace(Prompt, ""));
|
|
}
|
|
catch (XmlException e)
|
|
{
|
|
throw new NetConfServerException("Server capabilities received are not well formed XML", e);
|
|
}
|
|
|
|
var nsMgr = new XmlNamespaceManager(ServerCapabilities.NameTable);
|
|
nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
|
|
|
|
_usingFramingProtocol = (ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", nsMgr) != null);
|
|
|
|
_serverCapabilitiesConfirmed.Set();
|
|
}
|
|
else if (_usingFramingProtocol)
|
|
{
|
|
var position = 0;
|
|
|
|
for (; ; )
|
|
{
|
|
var match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
|
|
if (!match.Success)
|
|
{
|
|
break;
|
|
}
|
|
var fractionLength = Convert.ToInt32(match.Groups["length"].Value);
|
|
_rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength);
|
|
position += match.Index + match.Length + fractionLength;
|
|
}
|
|
if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
|
|
{
|
|
_rpcReplyReceived.Set();
|
|
}
|
|
}
|
|
else // Old protocol
|
|
{
|
|
_data.Append(chunk);
|
|
|
|
if (!chunk.Contains(Prompt))
|
|
{
|
|
return;
|
|
//throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt);
|
|
}
|
|
|
|
chunk = _data.ToString();
|
|
_data.Clear();
|
|
|
|
_rpcReply.Append(chunk.Replace(Prompt, ""));
|
|
_rpcReplyReceived.Set();
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
|
|
if (disposing)
|
|
{
|
|
if (_serverCapabilitiesConfirmed != null)
|
|
{
|
|
_serverCapabilitiesConfirmed.Dispose();
|
|
_serverCapabilitiesConfirmed = null;
|
|
}
|
|
|
|
if (_rpcReplyReceived != null)
|
|
{
|
|
_rpcReplyReceived.Dispose();
|
|
_rpcReplyReceived = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|