PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/SharpSnmpLib/Messaging/SnmpMessageExtension.cs

#
C# | 633 lines | 402 code | 85 blank | 146 comment | 137 complexity | 0d99bb73f9b82a872f2884fc96384dd7 MD5 | raw file
Possible License(s): MIT, LGPL-2.1
  1. // SNMP message extension class.
  2. // Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors.
  3. //
  4. // This library is free software; you can redistribute it and/or
  5. // modify it under the terms of the GNU Lesser General Public
  6. // License as published by the Free Software Foundation; either
  7. // version 2.1 of the License, or (at your option) any later version.
  8. //
  9. // This library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. // Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public
  15. // License along with this library; if not, write to the Free Software
  16. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Globalization;
  20. using System.Net;
  21. using System.Net.Sockets;
  22. using Lextm.SharpSnmpLib.Security;
  23. namespace Lextm.SharpSnmpLib.Messaging
  24. {
  25. /// <summary>
  26. /// Extension methods for <see cref="ISnmpMessage"/>.
  27. /// </summary>
  28. public static class SnmpMessageExtension
  29. {
  30. /// <summary>
  31. /// Gets the <see cref="SnmpType"/>.
  32. /// </summary>
  33. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  34. /// <returns></returns>
  35. public static SnmpType TypeCode(this ISnmpMessage message)
  36. {
  37. if (message == null)
  38. {
  39. throw new ArgumentNullException("message");
  40. }
  41. return message.Pdu().TypeCode;
  42. }
  43. /// <summary>
  44. /// Variables.
  45. /// </summary>
  46. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  47. public static IList<Variable> Variables(this ISnmpMessage message)
  48. {
  49. if (message == null)
  50. {
  51. throw new ArgumentNullException("message");
  52. }
  53. var code = message.TypeCode();
  54. return code == SnmpType.Unknown ? new List<Variable>(0) : message.Scope.Pdu.Variables;
  55. }
  56. /// <summary>
  57. /// Request ID.
  58. /// </summary>
  59. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  60. public static int RequestId(this ISnmpMessage message)
  61. {
  62. if (message == null)
  63. {
  64. throw new ArgumentNullException("message");
  65. }
  66. return message.Scope.Pdu.RequestId.ToInt32();
  67. }
  68. /// <summary>
  69. /// Gets the message ID.
  70. /// </summary>
  71. /// <value>The message ID.</value>
  72. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  73. /// <remarks>For v3, message ID is different from request ID. For v1 and v2c, they are the same.</remarks>
  74. public static int MessageId(this ISnmpMessage message)
  75. {
  76. if (message == null)
  77. {
  78. throw new ArgumentNullException("message");
  79. }
  80. return message.Header == Header.Empty ? message.RequestId() : message.Header.MessageId;
  81. }
  82. /// <summary>
  83. /// PDU.
  84. /// </summary>
  85. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  86. public static ISnmpPdu Pdu(this ISnmpMessage message)
  87. {
  88. if (message == null)
  89. {
  90. throw new ArgumentNullException("message");
  91. }
  92. return message.Scope.Pdu;
  93. }
  94. /// <summary>
  95. /// Community name.
  96. /// </summary>
  97. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  98. public static OctetString Community(this ISnmpMessage message)
  99. {
  100. if (message == null)
  101. {
  102. throw new ArgumentNullException("message");
  103. }
  104. return message.Parameters.UserName;
  105. }
  106. /// <summary>
  107. /// Sends an <see cref="ISnmpMessage"/>.
  108. /// </summary>
  109. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  110. /// <param name="manager">Manager</param>
  111. public static void Send(this ISnmpMessage message, EndPoint manager)
  112. {
  113. if (message == null)
  114. {
  115. throw new ArgumentNullException("message");
  116. }
  117. if (manager == null)
  118. {
  119. throw new ArgumentNullException("manager");
  120. }
  121. var code = message.TypeCode();
  122. if ((code != SnmpType.TrapV1Pdu && code != SnmpType.TrapV2Pdu) && code != SnmpType.ReportPdu)
  123. {
  124. throw new InvalidOperationException(String.Format(
  125. CultureInfo.InvariantCulture,
  126. "not a trap message: {0}",
  127. code));
  128. }
  129. using (var socket = manager.GetSocket())
  130. {
  131. message.Send(manager, socket);
  132. }
  133. }
  134. /// <summary>
  135. /// Sends an <see cref="ISnmpMessage"/>.
  136. /// </summary>
  137. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  138. /// <param name="manager">Manager</param>
  139. /// <param name="socket">The socket.</param>
  140. public static void Send(this ISnmpMessage message, EndPoint manager, Socket socket)
  141. {
  142. if (message == null)
  143. {
  144. throw new ArgumentNullException("message");
  145. }
  146. if (socket == null)
  147. {
  148. throw new ArgumentNullException("socket");
  149. }
  150. if (manager == null)
  151. {
  152. throw new ArgumentNullException("manager");
  153. }
  154. var code = message.TypeCode();
  155. if ((code != SnmpType.TrapV1Pdu && code != SnmpType.TrapV2Pdu) && code != SnmpType.ReportPdu)
  156. {
  157. throw new InvalidOperationException(String.Format(
  158. CultureInfo.InvariantCulture,
  159. "not a trap message: {0}",
  160. code));
  161. }
  162. var bytes = message.ToBytes();
  163. socket.SendTo(bytes, 0, bytes.Length, SocketFlags.None, manager);
  164. }
  165. /// <summary>
  166. /// Sends an <see cref="ISnmpMessage"/>.
  167. /// </summary>
  168. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  169. /// <param name="manager">Manager</param>
  170. public static void SendAsync(this ISnmpMessage message, EndPoint manager)
  171. {
  172. if (message == null)
  173. {
  174. throw new ArgumentNullException("message");
  175. }
  176. if (manager == null)
  177. {
  178. throw new ArgumentNullException("manager");
  179. }
  180. var code = message.TypeCode();
  181. if ((code != SnmpType.TrapV1Pdu && code != SnmpType.TrapV2Pdu) && code != SnmpType.ReportPdu)
  182. {
  183. throw new InvalidOperationException(String.Format(
  184. CultureInfo.InvariantCulture,
  185. "not a trap message: {0}",
  186. code));
  187. }
  188. using (var socket = manager.GetSocket())
  189. {
  190. message.SendAsync(manager, socket);
  191. }
  192. }
  193. /// <summary>
  194. /// Sends an <see cref="ISnmpMessage"/>.
  195. /// </summary>
  196. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  197. /// <param name="manager">Manager</param>
  198. /// <param name="socket">The socket.</param>
  199. public static void SendAsync(this ISnmpMessage message, EndPoint manager, Socket socket)
  200. {
  201. if (message == null)
  202. {
  203. throw new ArgumentNullException("message");
  204. }
  205. if (socket == null)
  206. {
  207. throw new ArgumentNullException("socket");
  208. }
  209. if (manager == null)
  210. {
  211. throw new ArgumentNullException("manager");
  212. }
  213. var code = message.TypeCode();
  214. if ((code != SnmpType.TrapV1Pdu && code != SnmpType.TrapV2Pdu) && code != SnmpType.ReportPdu)
  215. {
  216. throw new InvalidOperationException(String.Format(
  217. CultureInfo.InvariantCulture,
  218. "not a trap message: {0}",
  219. code));
  220. }
  221. var bytes = message.ToBytes();
  222. socket.BeginSendTo(bytes, 0, bytes.Length, SocketFlags.None, manager, ar => socket.EndSendTo(ar), null);
  223. }
  224. /// <summary>
  225. /// Sends this <see cref="ISnmpMessage"/> and handles the response from agent.
  226. /// </summary>
  227. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  228. /// <param name="timeout">The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.</param>
  229. /// <param name="receiver">Port number.</param>
  230. /// <param name="registry">User registry.</param>
  231. /// <returns></returns>
  232. public static ISnmpMessage GetResponse(this ISnmpMessage request, int timeout, IPEndPoint receiver, UserRegistry registry)
  233. {
  234. // TODO: make more usage of UserRegistry.
  235. if (request == null)
  236. {
  237. throw new ArgumentNullException("request");
  238. }
  239. if (receiver == null)
  240. {
  241. throw new ArgumentNullException("receiver");
  242. }
  243. var code = request.TypeCode();
  244. if (code == SnmpType.TrapV1Pdu || code == SnmpType.TrapV2Pdu || code == SnmpType.ReportPdu)
  245. {
  246. throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not a request message: {0}", code));
  247. }
  248. using (var socket = receiver.GetSocket())
  249. {
  250. return request.GetResponse(timeout, receiver, registry, socket);
  251. }
  252. }
  253. /// <summary>
  254. /// Sends this <see cref="ISnmpMessage"/> and handles the response from agent.
  255. /// </summary>
  256. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  257. /// <param name="timeout">The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.</param>
  258. /// <param name="receiver">Port number.</param>
  259. /// <returns></returns>
  260. public static ISnmpMessage GetResponse(this ISnmpMessage request, int timeout, IPEndPoint receiver)
  261. {
  262. if (request == null)
  263. {
  264. throw new ArgumentNullException("request");
  265. }
  266. if (receiver == null)
  267. {
  268. throw new ArgumentNullException("receiver");
  269. }
  270. var code = request.TypeCode();
  271. if (code == SnmpType.TrapV1Pdu || code == SnmpType.TrapV2Pdu || code == SnmpType.ReportPdu)
  272. {
  273. throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not a request message: {0}", code));
  274. }
  275. using (var socket = receiver.GetSocket())
  276. {
  277. return request.GetResponse(timeout, receiver, socket);
  278. }
  279. }
  280. /// <summary>
  281. /// Sends this <see cref="ISnmpMessage"/> and handles the response from agent.
  282. /// </summary>
  283. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  284. /// <param name="timeout">The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.</param>
  285. /// <param name="receiver">Agent.</param>
  286. /// <param name="udpSocket">The UDP <see cref="Socket"/> to use to send/receive.</param>
  287. /// <returns></returns>
  288. public static ISnmpMessage GetResponse(this ISnmpMessage request, int timeout, IPEndPoint receiver, Socket udpSocket)
  289. {
  290. if (request == null)
  291. {
  292. throw new ArgumentNullException("request");
  293. }
  294. if (receiver == null)
  295. {
  296. throw new ArgumentNullException("receiver");
  297. }
  298. if (udpSocket == null)
  299. {
  300. throw new ArgumentNullException("udpSocket");
  301. }
  302. var registry = new UserRegistry();
  303. if (request.Version == VersionCode.V3)
  304. {
  305. registry.Add(request.Parameters.UserName, request.Privacy);
  306. }
  307. return request.GetResponse(timeout, receiver, registry, udpSocket);
  308. }
  309. /// <summary>
  310. /// Sends an <see cref="ISnmpMessage"/> and handles the response from agent.
  311. /// </summary>
  312. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  313. /// <param name="timeout">The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.</param>
  314. /// <param name="receiver">Agent.</param>
  315. /// <param name="udpSocket">The UDP <see cref="Socket"/> to use to send/receive.</param>
  316. /// <param name="registry">The user registry.</param>
  317. /// <returns></returns>
  318. public static ISnmpMessage GetResponse(this ISnmpMessage request, int timeout, IPEndPoint receiver, UserRegistry registry, Socket udpSocket)
  319. {
  320. if (request == null)
  321. {
  322. throw new ArgumentNullException("request");
  323. }
  324. if (udpSocket == null)
  325. {
  326. throw new ArgumentNullException("udpSocket");
  327. }
  328. if (receiver == null)
  329. {
  330. throw new ArgumentNullException("receiver");
  331. }
  332. if (registry == null)
  333. {
  334. throw new ArgumentNullException("registry");
  335. }
  336. var requestCode = request.TypeCode();
  337. if (requestCode == SnmpType.TrapV1Pdu || requestCode == SnmpType.TrapV2Pdu || requestCode == SnmpType.ReportPdu)
  338. {
  339. throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not a request message: {0}", requestCode));
  340. }
  341. var bytes = request.ToBytes();
  342. #if CF
  343. int bufSize = 8192;
  344. #else
  345. var bufSize = udpSocket.ReceiveBufferSize;
  346. #endif
  347. var reply = new byte[bufSize];
  348. // Whatever you change, try to keep the Send and the Receive close to each other.
  349. udpSocket.SendTo(bytes, receiver);
  350. #if !(CF)
  351. udpSocket.ReceiveTimeout = timeout;
  352. #endif
  353. int count;
  354. try
  355. {
  356. count = udpSocket.Receive(reply, 0, bufSize, SocketFlags.None);
  357. }
  358. catch (SocketException ex)
  359. {
  360. // FIXME: If you use a Mono build without the fix for this issue (https://bugzilla.novell.com/show_bug.cgi?id=599488), please uncomment this code.
  361. /*
  362. if (SnmpMessageExtension.IsRunningOnMono && ex.ErrorCode == 10035)
  363. {
  364. throw TimeoutException.Create(receiver.Address, timeout);
  365. }
  366. // */
  367. if (ex.ErrorCode == WSAETIMEDOUT)
  368. {
  369. throw TimeoutException.Create(receiver.Address, timeout);
  370. }
  371. throw;
  372. }
  373. // Passing 'count' is not necessary because ParseMessages should ignore it, but it offer extra safety (and would avoid an issue if parsing >1 response).
  374. var response = MessageFactory.ParseMessages(reply, 0, count, registry)[0];
  375. var responseCode = response.TypeCode();
  376. if (responseCode == SnmpType.ResponsePdu || responseCode == SnmpType.ReportPdu)
  377. {
  378. var requestId = request.MessageId();
  379. var responseId = response.MessageId();
  380. if (responseId != requestId)
  381. {
  382. throw OperationException.Create(String.Format(CultureInfo.InvariantCulture, "wrong response sequence: expected {0}, received {1}", requestId, responseId), receiver.Address);
  383. }
  384. return response;
  385. }
  386. throw OperationException.Create(String.Format(CultureInfo.InvariantCulture, "wrong response type: {0}", responseCode), receiver.Address);
  387. }
  388. /// <summary>
  389. /// Ends a pending asynchronous read.
  390. /// </summary>
  391. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  392. /// <param name="asyncResult">An <see cref="IAsyncResult"/> that stores state information and any user defined data for this asynchronous operation.</param>
  393. /// <returns></returns>
  394. public static ISnmpMessage EndGetResponse(this ISnmpMessage request, IAsyncResult asyncResult)
  395. {
  396. if (asyncResult == null)
  397. {
  398. throw new ArgumentNullException("asyncResult");
  399. }
  400. if (request == null)
  401. {
  402. throw new ArgumentNullException("request");
  403. }
  404. var ar = (SnmpMessageAsyncResult)asyncResult;
  405. var s = ar.WorkSocket;
  406. var count = s.EndReceive(ar.Inner);
  407. // Passing 'count' is not necessary because ParseMessages should ignore it, but it offer extra safety (and would avoid an issue if parsing >1 response).
  408. var response = MessageFactory.ParseMessages(ar.GetBuffer(), 0, count, ar.Users)[0];
  409. var responseCode = response.TypeCode();
  410. if (responseCode == SnmpType.ResponsePdu || responseCode == SnmpType.ReportPdu)
  411. {
  412. var requestId = request.MessageId();
  413. var responseId = response.MessageId();
  414. if (responseId != requestId)
  415. {
  416. throw OperationException.Create(String.Format(CultureInfo.InvariantCulture, "wrong response sequence: expected {0}, received {1}", requestId, responseId), ar.Receiver.Address);
  417. }
  418. return response;
  419. }
  420. throw OperationException.Create(String.Format(CultureInfo.InvariantCulture, "wrong response type: {0}", responseCode), ar.Receiver.Address);
  421. }
  422. /// <summary>
  423. /// Begins to asynchronously send an <see cref="ISnmpMessage"/> to an <see cref="IPEndPoint"/>.
  424. /// </summary>
  425. /// <param name="request">The <see cref="ISnmpMessage"/>.</param>
  426. /// <param name="receiver">Agent.</param>
  427. /// <param name="registry">The user registry.</param>
  428. /// <param name="udpSocket">The UDP <see cref="Socket"/> to use to send/receive.</param>
  429. /// <param name="callback">The callback.</param>
  430. /// <param name="state">The state object.</param>
  431. /// <returns></returns>
  432. public static IAsyncResult BeginGetResponse(this ISnmpMessage request, IPEndPoint receiver, UserRegistry registry, Socket udpSocket, AsyncCallback callback, object state)
  433. {
  434. if (request == null)
  435. {
  436. throw new ArgumentNullException("request");
  437. }
  438. if (udpSocket == null)
  439. {
  440. throw new ArgumentNullException("udpSocket");
  441. }
  442. if (receiver == null)
  443. {
  444. throw new ArgumentNullException("receiver");
  445. }
  446. if (registry == null)
  447. {
  448. throw new ArgumentNullException("registry");
  449. }
  450. var requestCode = request.TypeCode();
  451. if (requestCode == SnmpType.TrapV1Pdu || requestCode == SnmpType.TrapV2Pdu || requestCode == SnmpType.ReportPdu)
  452. {
  453. throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not a request message: {0}", requestCode));
  454. }
  455. // Whatever you change, try to keep the Send and the Receive close to each other.
  456. udpSocket.SendTo(request.ToBytes(), receiver);
  457. #if CF
  458. var bufferSize = 8192;
  459. #else
  460. var bufferSize = udpSocket.ReceiveBufferSize;
  461. #endif
  462. var buffer = new byte[bufferSize];
  463. var ar = udpSocket.BeginReceive(buffer, 0, bufferSize, SocketFlags.None, callback, state);
  464. return new SnmpMessageAsyncResult(ar, udpSocket, registry, receiver, buffer);
  465. }
  466. /// <summary>
  467. /// Tests if runnning on Mono.
  468. /// </summary>
  469. /// <returns></returns>
  470. public static bool IsRunningOnMono
  471. {
  472. get { return Type.GetType("Mono.Runtime") != null; }
  473. }
  474. /// <summary>
  475. /// Packs up the <see cref="ISnmpMessage"/>.
  476. /// </summary>
  477. /// <param name="message">The <see cref="ISnmpMessage"/>.</param>
  478. /// <returns></returns>
  479. internal static Sequence PackMessage(this ISnmpMessage message)
  480. {
  481. if (message == null)
  482. {
  483. throw new ArgumentNullException("message");
  484. }
  485. return ByteTool.PackMessage(
  486. message.Version,
  487. message.Header,
  488. message.Parameters,
  489. message.Privacy.GetScopeData(message.Header, message.Parameters, message.Scope.GetData(message.Version)));
  490. }
  491. /// <summary>
  492. /// Packs the message.
  493. /// </summary>
  494. /// <param name="version">The version.</param>
  495. /// <param name="data">The data.</param>
  496. /// <returns></returns>
  497. internal static Sequence PackMessage(VersionCode version, params ISnmpData[] data)
  498. {
  499. if (data == null)
  500. {
  501. throw new ArgumentNullException("data");
  502. }
  503. var collection = new List<ISnmpData>(1 + data.Length) { new Integer32((int)version) };
  504. collection.AddRange(data);
  505. return new Sequence(collection);
  506. }
  507. /// <summary>
  508. /// http://msdn.microsoft.com/en-us/library/ms740668(VS.85).aspx
  509. /// </summary>
  510. private const int WSAETIMEDOUT = 10060;
  511. private sealed class SnmpMessageAsyncResult : IAsyncResult
  512. {
  513. private readonly byte[] _buffer;
  514. public SnmpMessageAsyncResult(IAsyncResult inner, Socket socket, UserRegistry users, IPEndPoint receiver, byte[] buffer)
  515. {
  516. _buffer = buffer;
  517. WorkSocket = socket;
  518. Users = users;
  519. Receiver = receiver;
  520. Inner = inner;
  521. }
  522. public IAsyncResult Inner { get; private set; }
  523. public Socket WorkSocket { get; private set; }
  524. public UserRegistry Users { get; private set; }
  525. public byte[] GetBuffer()
  526. {
  527. return _buffer;
  528. }
  529. public IPEndPoint Receiver { get; private set; }
  530. public bool IsCompleted
  531. {
  532. get { return Inner.IsCompleted; }
  533. }
  534. public System.Threading.WaitHandle AsyncWaitHandle
  535. {
  536. get { return Inner.AsyncWaitHandle; }
  537. }
  538. public object AsyncState
  539. {
  540. get { return Inner.AsyncState; }
  541. }
  542. public bool CompletedSynchronously
  543. {
  544. get { return Inner.CompletedSynchronously; }
  545. }
  546. }
  547. }
  548. }