PageRenderTime 32ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/eExNetworkLibary/Sockets/IPSocket.cs

#
C# | 369 lines | 221 code | 59 blank | 89 comment | 42 complexity | 0d878b4b90842815472143fb30ef60ca MD5 | raw file
  1. // This source file is part of the eEx Network Library
  2. //
  3. // Author: Emanuel Jöbstl <emi@eex-dev.net>
  4. // Weblink: http://network.eex-dev.net
  5. // http://eex.codeplex.com
  6. //
  7. // Licensed under the GNU Library General Public License (LGPL)
  8. //
  9. // (c) eex-dev 2007-2011
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Text;
  13. using System.Net;
  14. using eExNetworkLibrary.IP;
  15. using System.IO;
  16. using eExNetworkLibrary.IP.V6;
  17. using System.Net.Sockets;
  18. namespace eExNetworkLibrary.Sockets
  19. {
  20. /// <summary>
  21. /// This class represents a network library IP socket implementation.
  22. /// </summary>
  23. [System.Diagnostics.DebuggerDisplay("{BindingInformation.ToString()}")]
  24. public class IPSocket : SocketBase, IPseudoHeaderSource
  25. {
  26. private Dictionary<uint, MemoryStream> dictIDFragmentBuffer;
  27. private Random rRandom;
  28. private int iIPVersion;
  29. public override bool IsOpen
  30. {
  31. get { return true; }
  32. }
  33. /// <summary>
  34. /// Gets or sets the MTU for this socket.
  35. /// </summary>
  36. public int MaximumTransmissionUnit { get; set; }
  37. /// <summary>
  38. /// Gets the local IPAddress to which this socket is bound.
  39. /// </summary>
  40. public IPAddress LocalBinding { get; private set; }
  41. /// <summary>
  42. /// Gets the remote IPAddress to which this socket is bound.
  43. /// </summary>
  44. public IPAddress RemoteBinding { get; private set; }
  45. /// <summary>
  46. /// Gets the protocl this socket belongs to
  47. /// </summary>
  48. public IPProtocol ProtocolBinding { get; private set; }
  49. /// <summary>
  50. /// Returns the IP pseudo header for the given frame.
  51. /// </summary>
  52. /// <param name="fFrame">The frame to calculate the pseudo-header for.</param>
  53. /// <returns>The pseudo header of the given frame.</returns>
  54. public byte[] GetPseudoHeader(Frame fFrame)
  55. {
  56. IPFrame ipFrame;
  57. if (iIPVersion == 4)
  58. {
  59. ipFrame = new IPv4Frame();
  60. }
  61. else if (iIPVersion == 6)
  62. {
  63. ipFrame = new IPv6Frame();
  64. }
  65. else
  66. {
  67. throw new ArgumentException("Only IPv4 or IPv6 addresses are supportet on the moment.");
  68. }
  69. ipFrame.DestinationAddress = RemoteBinding;
  70. ipFrame.SourceAddress = LocalBinding;
  71. ipFrame.Protocol = ProtocolBinding;
  72. ipFrame.EncapsulatedFrame = fFrame;
  73. return ipFrame.GetPseudoHeader();
  74. }
  75. /// <summary>
  76. /// Creates a new instance of this class
  77. /// </summary>
  78. /// <param name="ipaRemoteBinding">The remote address to bind this socket to</param>
  79. /// <param name="ipaLocalBinding">The local address to bind this socket to</param>
  80. /// <param name="ipPotocol">The protocl this socket belongs to</param>
  81. public IPSocket(IPAddress ipaRemoteBinding, IPAddress ipaLocalBinding, IPProtocol ipPotocol)
  82. {
  83. if (ipaRemoteBinding.AddressFamily != ipaLocalBinding.AddressFamily)
  84. {
  85. throw new ArgumentException("It is not possible to mix up addresses of different types.");
  86. }
  87. if (ipaRemoteBinding.AddressFamily == AddressFamily.InterNetworkV6)
  88. {
  89. iIPVersion = 6;
  90. }
  91. else if (ipaRemoteBinding.AddressFamily == AddressFamily.InterNetwork)
  92. {
  93. iIPVersion = 4;
  94. }
  95. else
  96. {
  97. throw new ArgumentException("Only IPv4 and IPv6 addresses are supported at the moment.");
  98. }
  99. rRandom = new Random();
  100. LocalBinding = ipaLocalBinding;
  101. ProtocolBinding = ipPotocol;
  102. RemoteBinding = ipaRemoteBinding;
  103. MaximumTransmissionUnit = 1500;
  104. dictIDFragmentBuffer = new Dictionary<uint, MemoryStream>();
  105. }
  106. /// <summary>
  107. /// Decapsulates the given IP frame if the binding of this socket matches the frame and invokes the FrameDecapsulated event when finished.
  108. /// <remarks>This mehtod also handles IP fragmentation</remarks>
  109. /// </summary>
  110. /// <param name="fFrame">The frame to process</param>
  111. /// <param name="bPush">A bool indicating whether the frame is delivered with a push flag</param>
  112. /// <returns>A bool indicating whether the given frame matched the binding of this socket</returns>
  113. public override bool PushUp(Frame fFrame, bool bPush)
  114. {
  115. if (!FrameTypes.IsIP(fFrame))
  116. fFrame = IPFrame.Create(fFrame.FrameBytes);
  117. IPFrame ipFrame = (IPFrame)fFrame;
  118. if (!ipFrame.SourceAddress.Equals(RemoteBinding)
  119. || !ipFrame.DestinationAddress.Equals(LocalBinding))
  120. {
  121. return false;
  122. }
  123. if (ipFrame.FrameType == FrameTypes.IPv4)
  124. {
  125. IPv4Frame ipv4Frame = (IPv4Frame)ipFrame;
  126. if (ipv4Frame.Protocol != this.ProtocolBinding)
  127. {
  128. return false;
  129. }
  130. if (ipv4Frame.EncapsulatedFrame != null)
  131. {
  132. if (ipv4Frame.PacketFlags.MoreFragments)
  133. {
  134. HandleFragment(bPush, ipv4Frame.PacketFlags.MoreFragments, ipv4Frame.Identification, ipv4Frame.FragmentOffset, ipv4Frame.EncapsulatedFrame.FrameBytes);
  135. }
  136. else
  137. {
  138. InvokeFrameDecapsulated(ipv4Frame.EncapsulatedFrame, bPush);
  139. }
  140. }
  141. return true;
  142. }
  143. else if (ipFrame.FrameType == FrameTypes.IPv6)
  144. {
  145. ProtocolParser.ParseCompleteFrame(fFrame);
  146. Frame fPayload = null;
  147. Frame ipHeader = fFrame;
  148. while (ipHeader.EncapsulatedFrame != null && ipHeader.EncapsulatedFrame is IIPHeader)
  149. {
  150. if (((IIPHeader)ipHeader).Protocol == this.ProtocolBinding)
  151. {
  152. fPayload = ipHeader.EncapsulatedFrame;
  153. break;
  154. }
  155. }
  156. if (fPayload == null)
  157. return false; //Wrong payload type :(
  158. FragmentExtensionHeader fragHeader = (FragmentExtensionHeader)ProtocolParser.GetFrameByType(fFrame, FragmentExtensionHeader.DefaultFrameType);
  159. if (fragHeader != null)
  160. {
  161. HandleFragment(bPush, fragHeader.MoreFragments, fragHeader.Identification, fragHeader.FragmentOffset, fPayload.FrameBytes);
  162. }
  163. else
  164. {
  165. InvokeFrameDecapsulated(fPayload, bPush);
  166. }
  167. }
  168. throw new ArgumentException("Only IPv4 and IPv6 frames are supported at the moment.");
  169. }
  170. private void HandleFragment(bool bPush, bool bMoreFragments, uint iIdentification, int iFragmentOffset, byte[] bPayload)
  171. {
  172. // Fragmentation handling
  173. if (!dictIDFragmentBuffer.ContainsKey(iIdentification))
  174. {
  175. dictIDFragmentBuffer.Add(iIdentification, new MemoryStream());
  176. }
  177. if (dictIDFragmentBuffer.ContainsKey(iIdentification))
  178. {
  179. // Fragmentation handling
  180. dictIDFragmentBuffer[iIdentification].Seek(iFragmentOffset * 8, SeekOrigin.Begin);
  181. dictIDFragmentBuffer[iIdentification].Write(bPayload, 0, bPayload.Length);
  182. if (!bMoreFragments)
  183. {
  184. byte[] bData = dictIDFragmentBuffer[iIdentification].ToArray();
  185. dictIDFragmentBuffer.Remove(iIdentification);
  186. InvokeFrameDecapsulated(new RawDataFrame(bData));
  187. }
  188. }
  189. }
  190. /// <summary>
  191. /// Encapsulates the given IP frame according to the binding of this socket and invokes the FrameEncapsulated event when finished.
  192. /// <remarks>This method also handles IP fragmentation</remarks>
  193. /// </summary>
  194. /// <param name="fFrame">The frame to process</param>
  195. /// <param name="bPush">A bool indicating whether the frame is delivered with a push flag</param>
  196. public override void PushDown(Frame fFrame, bool bPush)
  197. {
  198. if (iIPVersion == 4)
  199. {
  200. IPv4Frame ipv4Frame = new IPv4Frame();
  201. ipv4Frame.DestinationAddress = RemoteBinding;
  202. ipv4Frame.SourceAddress = LocalBinding;
  203. ipv4Frame.Protocol = ProtocolBinding;
  204. ipv4Frame.Identification = (uint)rRandom.Next(Int32.MaxValue);
  205. ipv4Frame.EncapsulatedFrame = fFrame;
  206. foreach (IPFrame fFragment in IPFragmenter.FragmentV4(ipv4Frame, MaximumTransmissionUnit))
  207. {
  208. InvokeFrameEncapsulated(fFragment, bPush);
  209. }
  210. }
  211. else if (iIPVersion == 6)
  212. {
  213. IPv6Frame ipv6Frame = new IPv6Frame();
  214. ipv6Frame.DestinationAddress = RemoteBinding;
  215. ipv6Frame.SourceAddress = LocalBinding;
  216. ipv6Frame.Protocol = ProtocolBinding;
  217. ipv6Frame.EncapsulatedFrame = fFrame;
  218. foreach (IPFrame fFragment in IPFragmenter.FragmentV6(ipv6Frame, MaximumTransmissionUnit))
  219. {
  220. InvokeFrameEncapsulated(fFragment, bPush);
  221. }
  222. }
  223. else
  224. {
  225. throw new ArgumentException("Only IPv4 and IPv6 addresses are supportet on the moment.");
  226. }
  227. }
  228. /// <summary>
  229. /// Returns the BindingInformation of this socket as IPBindingInformation
  230. /// </summary>
  231. public override BindingInformation BindingInformation
  232. {
  233. get { return new IPBindingInformation(new IPEndPoint(LocalBinding), new IPEndPoint(RemoteBinding), ProtocolBinding); }
  234. }
  235. public override void Close()
  236. {
  237. dictIDFragmentBuffer.Clear();
  238. base.Close();
  239. }
  240. public override void Dispose()
  241. {
  242. dictIDFragmentBuffer.Clear();
  243. }
  244. public override void Flush()
  245. {
  246. //Nothing to do
  247. }
  248. }
  249. /// <summary>
  250. /// This class represents IP socket binding information
  251. /// </summary>
  252. public class IPBindingInformation : BindingInformation
  253. {
  254. /// <summary>
  255. /// Gets the protocl the socket belongs to
  256. /// </summary>
  257. ///
  258. public IPProtocol ProtocolBinding { get; private set; }
  259. /// <summary>
  260. /// Creates a new instance of this class
  261. /// </summary>
  262. /// <param name="localBinding">The local binding information</param>
  263. /// <param name="remoteBinding">The remote binding Information</param>
  264. /// <param name="protocolBinding">The protocol binding Information</param>
  265. public IPBindingInformation(IPEndPoint localBinding, IPEndPoint remoteBinding, IPProtocol protocolBinding)
  266. : base(localBinding, remoteBinding)
  267. {
  268. ProtocolBinding = protocolBinding;
  269. }
  270. /// <summary>
  271. /// Gets the description of this EndPoint
  272. /// </summary>
  273. /// <returns>The description of this EndPoint</returns>
  274. public override string ToString()
  275. {
  276. return base.ToString() + ", " + ProtocolBinding.ToString() ;
  277. }
  278. }
  279. /// <summary>
  280. /// This class represents an IP endpoint
  281. /// </summary>
  282. public class IPEndPoint : EndPoint
  283. {
  284. /// <summary>
  285. /// Creates a new IP endpoint
  286. /// </summary>
  287. /// <param name="ipaBinding">The address this IP endpoint belongs to</param>
  288. public IPEndPoint(IPAddress ipaBinding) :
  289. base(ipaBinding.ToString())
  290. {
  291. Address = ipaBinding;
  292. }
  293. /// <summary>
  294. /// Returns the address of the IP endpoint
  295. /// </summary>
  296. public IPAddress Address { get; private set; }
  297. }
  298. /// <summary>
  299. /// Represents an interface for classes which support pseudo-header generation for checksum calculation.
  300. /// </summary>
  301. public interface IPseudoHeaderSource
  302. {
  303. /// <summary>
  304. /// Has to return a pseudo-header to calculate the TCP checksum from for the given frame.
  305. /// </summary>
  306. /// <param name="fFrame">The frame to calculate the pseudo-header for.</param>
  307. /// <returns>The pseudo-header for the given frame.</returns>
  308. byte[] GetPseudoHeader(Frame fFrame);
  309. }
  310. }