NetConfSession.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using Renci.SshNet.Channels;
  7. using Renci.SshNet.Common;
  8. using System.Diagnostics;
  9. using System.Collections.Generic;
  10. using System.Globalization;
  11. using Renci.SshNet.Sftp.Responses;
  12. using Renci.SshNet.Sftp.Requests;
  13. using Renci.SshNet.Sftp;
  14. using Renci.SshNet.Messages.Connection;
  15. using System.Xml;
  16. using System.Text.RegularExpressions;
  17. namespace Renci.SshNet.NetConf
  18. {
  19. internal class NetConfSession : SubsystemSession
  20. {
  21. private bool _usingFramingProtocol = false;
  22. private const string _prompt = "]]>]]>";
  23. private List<byte> _data = new List<byte>(32 * 1024);
  24. private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(false);
  25. private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(false);
  26. private StringBuilder _rpcReply = new StringBuilder();
  27. private int _messageId = 0;
  28. /// <summary>
  29. /// Gets NetConf server capabilities.
  30. /// </summary>
  31. public XmlDocument ServerCapabilities { get; private set; }
  32. /// <summary>
  33. /// Gets NetConf client capabilities.
  34. /// </summary>
  35. public XmlDocument ClientCapabilities { get; private set; }
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="NetConfSession"/> class.
  38. /// </summary>
  39. /// <param name="session">The session.</param>
  40. /// <param name="operationTimeout">The operation timeout.</param>
  41. public NetConfSession(Session session, TimeSpan operationTimeout)
  42. : base(session, "netconf", operationTimeout)
  43. {
  44. ClientCapabilities = new XmlDocument();
  45. ClientCapabilities.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
  46. "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
  47. "<capabilities>" +
  48. "<capability>" +
  49. "urn:ietf:params:netconf:base:1.0" +
  50. "</capability>" +
  51. "</capabilities>" +
  52. "</hello>");
  53. }
  54. public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling)
  55. {
  56. XmlNamespaceManager ns = null;
  57. if (automaticMessageIdHandling)
  58. {
  59. _messageId++;
  60. ns = new XmlNamespaceManager(rpc.NameTable);
  61. ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
  62. rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value = _messageId.ToString();
  63. }
  64. _rpcReply = new StringBuilder();
  65. _rpcReplyReceived.Reset();
  66. var reply = new XmlDocument();
  67. if (_usingFramingProtocol)
  68. {
  69. StringBuilder command = new StringBuilder(rpc.InnerXml.Length + 10);
  70. command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length);
  71. command.Append(rpc.InnerXml);
  72. command.Append("\n##\n");
  73. SendData(new ChannelDataMessage(this.ChannelNumber, Encoding.UTF8.GetBytes(command.ToString())));
  74. this.WaitHandle(this._rpcReplyReceived, this._operationTimeout);
  75. reply.LoadXml(_rpcReply.ToString());
  76. }
  77. else
  78. {
  79. SendData(new ChannelDataMessage(this.ChannelNumber, Encoding.UTF8.GetBytes(rpc.InnerXml + _prompt)));
  80. this.WaitHandle(this._rpcReplyReceived, this._operationTimeout);
  81. reply.LoadXml(_rpcReply.ToString());
  82. }
  83. if (automaticMessageIdHandling)
  84. {
  85. //string reply_id = rpc.SelectSingleNode("/nc:rpc-reply/@message-id", ns).Value;
  86. string reply_id = rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value;
  87. if (reply_id != _messageId.ToString())
  88. {
  89. throw new NetConfServerException("The rpc message id does not match the rpc-reply message id.");
  90. }
  91. }
  92. return reply;
  93. }
  94. protected override void OnChannelOpen()
  95. {
  96. string message = string.Format("{0}{1}", this.ClientCapabilities.InnerXml, _prompt);
  97. this.SendData(new ChannelDataMessage(this.ChannelNumber, Encoding.UTF8.GetBytes(message)));
  98. this.WaitHandle(this._serverCapabilitiesConfirmed, this._operationTimeout);
  99. }
  100. protected override void OnDataReceived(uint dataTypeCode, byte[] data)
  101. {
  102. string chunk = Encoding.UTF8.GetString(data);
  103. if (this.ServerCapabilities == null) // This must be server capabilities, old protocol
  104. {
  105. if (!chunk.EndsWith(_prompt))
  106. {
  107. throw new NetConfServerException("Server capabilities is not the first message received");
  108. }
  109. try
  110. {
  111. this.ServerCapabilities = new XmlDocument();
  112. this.ServerCapabilities.LoadXml(chunk.Replace(_prompt, ""));
  113. }
  114. catch (XmlException e)
  115. {
  116. throw new NetConfServerException("Server capabilities received are not well formed XML", e);
  117. }
  118. XmlNamespaceManager ns = new XmlNamespaceManager(this.ServerCapabilities.NameTable);
  119. ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
  120. this._usingFramingProtocol = (this.ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", ns) != null);
  121. this._serverCapabilitiesConfirmed.Set();
  122. }
  123. else if (this._usingFramingProtocol)
  124. {
  125. int position = 0;
  126. for (; ; )
  127. {
  128. Match match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
  129. if (!match.Success)
  130. {
  131. break;
  132. }
  133. int fractionLength = Convert.ToInt32(match.Groups["length"].Value);
  134. this._rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength);
  135. position += match.Index + match.Length + fractionLength;
  136. }
  137. if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
  138. {
  139. this._rpcReplyReceived.Set();
  140. }
  141. }
  142. else // Old protocol
  143. {
  144. if (!chunk.EndsWith(_prompt))
  145. {
  146. throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt);
  147. }
  148. this._rpcReply.Append(chunk.Replace(_prompt, ""));
  149. this._rpcReplyReceived.Set();
  150. }
  151. }
  152. protected override void Dispose(bool disposing)
  153. {
  154. base.Dispose(disposing);
  155. if (disposing)
  156. {
  157. if (this._serverCapabilitiesConfirmed != null)
  158. {
  159. this._serverCapabilitiesConfirmed.Dispose();
  160. this._serverCapabilitiesConfirmed = null;
  161. }
  162. if (this._rpcReplyReceived != null)
  163. {
  164. this._rpcReplyReceived.Dispose();
  165. this._rpcReplyReceived = null;
  166. }
  167. }
  168. }
  169. }
  170. }