NetConfSession.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 StringBuilder _data = new StringBuilder();
  22. private bool _usingFramingProtocol = false;
  23. private const string _prompt = "]]>]]>";
  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, Encoding.UTF8)
  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. this._data.Clear();
  57. XmlNamespaceManager ns = null;
  58. if (automaticMessageIdHandling)
  59. {
  60. _messageId++;
  61. ns = new XmlNamespaceManager(rpc.NameTable);
  62. ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
  63. rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value = _messageId.ToString();
  64. }
  65. _rpcReply = new StringBuilder();
  66. _rpcReplyReceived.Reset();
  67. var reply = new XmlDocument();
  68. if (_usingFramingProtocol)
  69. {
  70. StringBuilder command = new StringBuilder(rpc.InnerXml.Length + 10);
  71. command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length);
  72. command.Append(rpc.InnerXml);
  73. command.Append("\n##\n");
  74. this.SendData(Encoding.UTF8.GetBytes(command.ToString()));
  75. this.WaitHandle(this._rpcReplyReceived, this._operationTimeout);
  76. reply.LoadXml(_rpcReply.ToString());
  77. }
  78. else
  79. {
  80. this.SendData(Encoding.UTF8.GetBytes(rpc.InnerXml + _prompt));
  81. this.WaitHandle(this._rpcReplyReceived, this._operationTimeout);
  82. reply.LoadXml(_rpcReply.ToString());
  83. }
  84. if (automaticMessageIdHandling)
  85. {
  86. //string reply_id = rpc.SelectSingleNode("/nc:rpc-reply/@message-id", ns).Value;
  87. string reply_id = rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value;
  88. if (reply_id != _messageId.ToString())
  89. {
  90. throw new NetConfServerException("The rpc message id does not match the rpc-reply message id.");
  91. }
  92. }
  93. return reply;
  94. }
  95. protected override void OnChannelOpen()
  96. {
  97. this._data.Clear();
  98. string message = string.Format("{0}{1}", this.ClientCapabilities.InnerXml, _prompt);
  99. this.SendData(Encoding.UTF8.GetBytes(message));
  100. this.WaitHandle(this._serverCapabilitiesConfirmed, this._operationTimeout);
  101. }
  102. protected override void OnDataReceived(uint dataTypeCode, byte[] data)
  103. {
  104. string chunk = Encoding.UTF8.GetString(data);
  105. if (this.ServerCapabilities == null) // This must be server capabilities, old protocol
  106. {
  107. this._data.Append(chunk);
  108. if (!chunk.Contains(_prompt))
  109. {
  110. return;
  111. }
  112. try
  113. {
  114. chunk = this._data.ToString();
  115. this._data.Clear();
  116. this.ServerCapabilities = new XmlDocument();
  117. this.ServerCapabilities.LoadXml(chunk.Replace(_prompt, ""));
  118. }
  119. catch (XmlException e)
  120. {
  121. throw new NetConfServerException("Server capabilities received are not well formed XML", e);
  122. }
  123. XmlNamespaceManager ns = new XmlNamespaceManager(this.ServerCapabilities.NameTable);
  124. ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0");
  125. this._usingFramingProtocol = (this.ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", ns) != null);
  126. this._serverCapabilitiesConfirmed.Set();
  127. }
  128. else if (this._usingFramingProtocol)
  129. {
  130. int position = 0;
  131. for (; ; )
  132. {
  133. Match match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
  134. if (!match.Success)
  135. {
  136. break;
  137. }
  138. int fractionLength = Convert.ToInt32(match.Groups["length"].Value);
  139. this._rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength);
  140. position += match.Index + match.Length + fractionLength;
  141. }
  142. if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
  143. {
  144. this._rpcReplyReceived.Set();
  145. }
  146. }
  147. else // Old protocol
  148. {
  149. this._data.Append(chunk);
  150. if (!chunk.Contains(_prompt))
  151. {
  152. return;
  153. //throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt);
  154. }
  155. chunk = this._data.ToString();
  156. this._data.Clear();
  157. this._rpcReply.Append(chunk.Replace(_prompt, ""));
  158. this._rpcReplyReceived.Set();
  159. }
  160. }
  161. protected override void Dispose(bool disposing)
  162. {
  163. base.Dispose(disposing);
  164. if (disposing)
  165. {
  166. if (this._serverCapabilitiesConfirmed != null)
  167. {
  168. this._serverCapabilitiesConfirmed.Dispose();
  169. this._serverCapabilitiesConfirmed = null;
  170. }
  171. if (this._rpcReplyReceived != null)
  172. {
  173. this._rpcReplyReceived.Dispose();
  174. this._rpcReplyReceived = null;
  175. }
  176. }
  177. }
  178. }
  179. }