├── HolePunching.v11.suo ├── TcpHolePunching ├── TcpHolePunching.suo ├── Peer │ ├── bin │ │ └── Debug │ │ │ ├── Peer.exe │ │ │ ├── Peer.pdb │ │ │ ├── Peer.vshost.exe │ │ │ ├── TcpHolePunching.dll │ │ │ ├── TcpHolePunching.pdb │ │ │ └── Peer.vshost.exe.manifest │ ├── obj │ │ └── x86 │ │ │ └── Debug │ │ │ ├── Peer.exe │ │ │ ├── Peer.pdb │ │ │ ├── Peer.csprojResolveAssemblyReference.cache │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ └── Peer.csproj.FileListAbsolute.txt │ ├── NLog.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Peer.csproj │ └── Program.cs ├── TcpHolePunching.v11.suo ├── IdealPeer │ ├── bin │ │ └── Debug │ │ │ ├── IdealPeer.exe │ │ │ ├── IdealPeer.pdb │ │ │ ├── IdealClient.exe │ │ │ ├── IdealClient.pdb │ │ │ ├── TcpHolePunching.dll │ │ │ ├── TcpHolePunching.pdb │ │ │ ├── IdealPeer.vshost.exe │ │ │ └── IdealPeer.vshost.exe.manifest │ ├── obj │ │ └── Debug │ │ │ ├── IdealPeer.exe │ │ │ ├── IdealPeer.pdb │ │ │ ├── IdealClient.exe │ │ │ ├── IdealClient.pdb │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ ├── IdealPeer.csprojResolveAssemblyReference.cache │ │ │ ├── IdealClient.csprojResolveAssemblyReference.cache │ │ │ ├── IdealPeer.csproj.FileListAbsolute.txt │ │ │ └── IdealClient.csproj.FileListAbsolute.txt │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Program.cs │ └── IdealClient.csproj ├── TcpHolePunching │ ├── bin │ │ └── Debug │ │ │ ├── NLog.dll │ │ │ ├── TcpHolePunching.dll │ │ │ ├── TcpHolePunching.pdb │ │ │ ├── TcpHolePunching.vshost.exe │ │ │ └── TcpHolePunching.vshost.exe.manifest │ ├── obj │ │ └── x86 │ │ │ └── Debug │ │ │ ├── TcpHolePunching.dll │ │ │ ├── TcpHolePunching.pdb │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ ├── TcpHolePunching.csprojResolveAssemblyReference.cache │ │ │ └── TcpHolePunching.csproj.FileListAbsolute.txt │ ├── packages.config │ ├── Messages │ │ ├── Message.cs │ │ ├── ResponseIntroducerRegistrationMessage.cs │ │ ├── RequestIntroducerRegistrationMessage.cs │ │ ├── RequestIntroducerIntroductionMessage.cs │ │ ├── MessageBase.cs │ │ └── ResponseIntroducerIntroductionMessage.cs │ ├── MessageSentEventArgs.cs │ ├── ConnectionAcceptedEventArgs.cs │ ├── Registrant.cs │ ├── NLog.config │ ├── MessageReceivedEventArgs.cs │ ├── MessageExtensions.cs │ ├── Client.cs │ ├── ISerializable.cs │ ├── NetworkPeer.cs │ ├── StringExtensions.cs │ ├── TcpHolePunching.csproj │ ├── IValueReader.cs │ ├── StreamValueReader.cs │ ├── ISerializer.cs │ ├── StreamValueWriter.cs │ ├── IValueWriter.cs │ ├── BufferValueReader.cs │ ├── NetworkClient.cs │ ├── NetworkIntroducer.cs │ ├── SerializerExtensions.cs │ ├── ObjectSerializer.cs │ └── BufferValueWriter.cs ├── IdealServer │ ├── bin │ │ └── Debug │ │ │ ├── IdealServer.exe │ │ │ ├── IdealServer.pdb │ │ │ ├── TcpHolePunching.dll │ │ │ └── TcpHolePunching.pdb │ ├── obj │ │ └── Debug │ │ │ ├── IdealServer.exe │ │ │ ├── IdealServer.pdb │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ ├── IdealServer.csprojResolveAssemblyReference.cache │ │ │ └── IdealServer.csproj.FileListAbsolute.txt │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Program.cs │ └── IdealServer.csproj ├── Introducer │ ├── bin │ │ └── Debug │ │ │ ├── Introducer.exe │ │ │ ├── Introducer.pdb │ │ │ ├── Introducer.vshost.exe │ │ │ ├── TcpHolePunching.dll │ │ │ ├── TcpHolePunching.pdb │ │ │ └── Introducer.vshost.exe.manifest │ ├── obj │ │ └── x86 │ │ │ └── Debug │ │ │ ├── Introducer.exe │ │ │ ├── Introducer.pdb │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ ├── Introducer.csprojResolveAssemblyReference.cache │ │ │ └── Introducer.csproj.FileListAbsolute.txt │ ├── NLog.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Introducer.csproj │ └── Program.cs ├── packages │ ├── NLog.2.0.0.2000 │ │ ├── lib │ │ │ ├── sl2 │ │ │ │ └── NLog.dll │ │ │ ├── sl3 │ │ │ │ └── NLog.dll │ │ │ ├── sl4 │ │ │ │ └── NLog.dll │ │ │ ├── net20 │ │ │ │ └── NLog.dll │ │ │ ├── net35 │ │ │ │ └── NLog.dll │ │ │ ├── net40 │ │ │ │ └── NLog.dll │ │ │ ├── sl3-wp │ │ │ │ └── NLog.dll │ │ │ └── sl4-windowsphone71 │ │ │ │ └── NLog.dll │ │ └── NLog.2.0.0.2000.nupkg │ └── repositories.config ├── TcpHolePunching.sln.DotSettings.user ├── TcpHolePunching.sln └── TcpHolePunching.6.0.ReSharper.user └── README.md /HolePunching.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/HolePunching.v11.suo -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching.suo -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/Peer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/bin/Debug/Peer.exe -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/Peer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/bin/Debug/Peer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching.v11.suo -------------------------------------------------------------------------------- /TcpHolePunching/Peer/obj/x86/Debug/Peer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/obj/x86/Debug/Peer.exe -------------------------------------------------------------------------------- /TcpHolePunching/Peer/obj/x86/Debug/Peer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/obj/x86/Debug/Peer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/Peer.vshost.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/bin/Debug/Peer.vshost.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/bin/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/bin/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/bin/Debug/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/bin/Debug/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealClient.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/IdealClient.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealClient.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/IdealClient.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealClient.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealClient.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealClient.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealClient.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/bin/Debug/IdealServer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/bin/Debug/IdealServer.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/bin/Debug/IdealServer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/bin/Debug/IdealServer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/obj/Debug/IdealServer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/obj/Debug/IdealServer.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/obj/Debug/IdealServer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/obj/Debug/IdealServer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/Introducer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/bin/Debug/Introducer.exe -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/Introducer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/bin/Debug/Introducer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/obj/x86/Debug/Introducer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/obj/x86/Debug/Introducer.exe -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/obj/x86/Debug/Introducer.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/obj/x86/Debug/Introducer.pdb -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.vshost.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.vshost.exe -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/bin/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/bin/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/bin/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/bin/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/Introducer.vshost.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/bin/Debug/Introducer.vshost.exe -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/bin/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/bin/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl2/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl2/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl3/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl3/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl4/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl4/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/net20/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/net20/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/net35/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/net35/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/net40/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/net40/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl3-wp/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl3-wp/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/NLog.2.0.0.2000.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/NLog.2.0.0.2000.nupkg -------------------------------------------------------------------------------- /TcpHolePunching/packages/repositories.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.dll -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.pdb -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.vshost.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.vshost.exe -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl4-windowsphone71/NLog.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/packages/NLog.2.0.0.2000/lib/sl4-windowsphone71/NLog.dll -------------------------------------------------------------------------------- /TcpHolePunching/Peer/obj/x86/Debug/Peer.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/obj/x86/Debug/Peer.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/Peer/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Peer/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealClient.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealPeer/obj/Debug/IdealClient.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/obj/Debug/IdealServer.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/IdealServer/obj/Debug/IdealServer.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/obj/x86/Debug/Introducer.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/Introducer/obj/x86/Debug/Introducer.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonpang/tcp-holepunching/HEAD/TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TcpHolePunching.Messages 7 | { 8 | public class Message : MessageBase 9 | { 10 | public Message() 11 | : base(MessageType.Internal) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/MessageSentEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching 8 | { 9 | public class MessageSentEventArgs : EventArgs 10 | { 11 | public EndPoint To { get; set; } 12 | public int Length { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/ConnectionAcceptedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | 8 | namespace TcpHolePunching 9 | { 10 | public class ConnectionAcceptedEventArgs : EventArgs 11 | { 12 | public Socket Socket { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Registrant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching 8 | { 9 | public class Registrant 10 | { 11 | public IPEndPoint InternalEndPoint { get; set; } 12 | public IPEndPoint ExternalEndPoint { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TcpHolePunching/Peer/NLog.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/NLog.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/NLog.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/MessageReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using TcpHolePunching.Messages; 7 | 8 | namespace TcpHolePunching 9 | { 10 | public class MessageReceivedEventArgs : EventArgs 11 | { 12 | public IPEndPoint From { get; set; } 13 | public MessageType MessageType { get; set; } 14 | public IValueReader MessageReader { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TcpHolePunching/Peer/bin/Debug/Peer.vshost.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/bin/Debug/IdealPeer.vshost.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/bin/Debug/Introducer.vshost.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/bin/Debug/TcpHolePunching.vshost.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 | 2 | False 3 | Never 4 | True -------------------------------------------------------------------------------- /TcpHolePunching/Peer/obj/x86/Debug/Peer.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\bin\Debug\Peer.exe 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\bin\Debug\Peer.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\bin\Debug\TcpHolePunching.dll 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\bin\Debug\TcpHolePunching.pdb 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\obj\x86\Debug\Peer.exe 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\obj\x86\Debug\Peer.pdb 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Peer\obj\x86\Debug\Peer.csprojResolveAssemblyReference.cache 8 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using TcpHolePunching.Messages; 6 | 7 | namespace TcpHolePunching 8 | { 9 | public static class MessageExtensions 10 | { 11 | public static byte[] GetBytes (this MessageBase messageBase) 12 | { 13 | var writer = new BufferValueWriter(new byte[1024]); 14 | messageBase.WritePayload(writer); 15 | 16 | var resizedBuffer = new byte[writer.Length]; 17 | Buffer.BlockCopy(writer.Buffer, 0, resizedBuffer, 0, resizedBuffer.Length); 18 | 19 | return resizedBuffer; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealPeer.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\IdealPeer.exe 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\IdealPeer.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\TcpHolePunching.dll 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\TcpHolePunching.pdb 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealPeer.csprojResolveAssemblyReference.cache 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealPeer.exe 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealPeer.pdb 8 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/obj/Debug/IdealClient.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\IdealClient.exe 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\IdealClient.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\TcpHolePunching.dll 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\bin\Debug\TcpHolePunching.pdb 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealClient.csprojResolveAssemblyReference.cache 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealClient.exe 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealPeer\obj\Debug\IdealClient.pdb 8 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/obj/Debug/IdealServer.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\bin\Debug\IdealServer.exe 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\bin\Debug\IdealServer.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\bin\Debug\TcpHolePunching.dll 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\bin\Debug\TcpHolePunching.pdb 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\obj\Debug\IdealServer.csprojResolveAssemblyReference.cache 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\obj\Debug\IdealServer.exe 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\IdealServer\obj\Debug\IdealServer.pdb 8 | -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/obj/x86/Debug/Introducer.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\bin\Debug\Introducer.exe 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\bin\Debug\Introducer.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\obj\x86\Debug\Introducer.exe 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\obj\x86\Debug\Introducer.pdb 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\bin\Debug\TcpHolePunching.dll 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\bin\Debug\TcpHolePunching.pdb 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\Introducer\obj\x86\Debug\Introducer.csprojResolveAssemblyReference.cache 8 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/obj/x86/Debug/TcpHolePunching.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\bin\Debug\TcpHolePunching.dll 2 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\bin\Debug\TcpHolePunching.pdb 3 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\bin\Debug\NLog.dll 4 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\bin\Debug\NLog.xml 5 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\obj\x86\Debug\TcpHolePunching.dll 6 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\obj\x86\Debug\TcpHolePunching.pdb 7 | C:\Users\Jason\Dropbox\TcpHolePunching\TcpHolePunching\TcpHolePunching\obj\x86\Debug\TcpHolePunching.csprojResolveAssemblyReference.cache 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TCP Hole-Punching 2 | ================ 3 | 4 | Purpose 5 | ----------- 6 | 7 | To demonstrate hole punching / NAT traversal using the TCP protocol. 8 | 9 | UDP hole punching is more well known and TCP hole punching is less supported by different router models, so this is a proof of concept. 10 | 11 | Instructions 12 | ------------ 13 | 14 | 1. Run the Introducer executable on a publicy reachable server (e.g. an Amazon EC2 instance). 15 | 16 | 2. Run the Peer executable on both PCs you want to connect. 17 | 18 | 3. Use a service like cmyip.com to determine the public WAN IPs of each PC, and enter them in each Peer. Hit . 19 | 20 | If the connection succeeds, then TCP hole punching just succeeded. 21 | 22 | * It helps to lower the firewalls on each PC. 23 | * This will most likely not work if either PC is in a corporate network. 24 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | 8 | namespace TcpHolePunching 9 | { 10 | public class Client 11 | { 12 | /// 13 | /// The socket that belongs to the client. 14 | /// 15 | public Socket Socket { get; set; } 16 | 17 | /// 18 | /// The buffer in which to receive bytes from the transport. 19 | /// 20 | public byte[] Buffer { get; set; } 21 | 22 | public Client(Socket socket) 23 | { 24 | Socket = socket; 25 | Buffer = new byte[1024]; 26 | } 27 | 28 | public EndPoint RemoteEndPoint 29 | { 30 | get 31 | { 32 | if (Socket == null) 33 | return null; 34 | 35 | return Socket.RemoteEndPoint; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/ResponseIntroducerRegistrationMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching.Messages 8 | { 9 | public class ResponseIntroducerRegistrationMessage : MessageBase 10 | { 11 | public IPEndPoint RegisteredEndPoint { get; set; } 12 | 13 | public ResponseIntroducerRegistrationMessage() 14 | : base(MessageType.ResponseIntroducerRegistration) 15 | { 16 | } 17 | 18 | public override void WritePayload(IValueWriter writer) 19 | { 20 | base.WritePayload(writer); 21 | writer.WriteBytes(RegisteredEndPoint.Address.GetAddressBytes()); 22 | writer.WriteInt32(RegisteredEndPoint.Port); 23 | } 24 | 25 | public override void ReadPayload(IValueReader reader) 26 | { 27 | base.ReadPayload(reader); 28 | var endPointAddress = new IPAddress(reader.ReadBytes()); 29 | RegisteredEndPoint = new IPEndPoint(endPointAddress, reader.ReadInt32()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/RequestIntroducerRegistrationMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching.Messages 8 | { 9 | public class RequestIntroducerRegistrationMessage : MessageBase 10 | { 11 | public IPEndPoint InternalClientEndPoint { get; set; } 12 | 13 | public RequestIntroducerRegistrationMessage() 14 | : base(MessageType.RequestIntroducerRegistration) 15 | { 16 | } 17 | 18 | public override void WritePayload(IValueWriter writer) 19 | { 20 | base.WritePayload(writer); 21 | writer.WriteBytes(InternalClientEndPoint.Address.GetAddressBytes()); 22 | writer.WriteInt32(InternalClientEndPoint.Port); 23 | } 24 | 25 | public override void ReadPayload(IValueReader reader) 26 | { 27 | base.ReadPayload(reader); 28 | var endPointAddress = new IPAddress(reader.ReadBytes()); 29 | InternalClientEndPoint = new IPEndPoint(endPointAddress, reader.ReadInt32()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/RequestIntroducerIntroductionMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching.Messages 8 | { 9 | public class RequestIntroducerIntroductionMessage : MessageBase 10 | { 11 | public IPEndPoint InternalOwnEndPoint { get; set; } 12 | public IPEndPoint ExternalPeerEndPoint { get; set; } 13 | 14 | public RequestIntroducerIntroductionMessage() 15 | : base(MessageType.RequestIntroducerIntroduction) 16 | { 17 | } 18 | 19 | public override void WritePayload(IValueWriter writer) 20 | { 21 | base.WritePayload(writer); 22 | writer.WriteBytes(InternalOwnEndPoint.Address.GetAddressBytes()); 23 | writer.WriteInt32(InternalOwnEndPoint.Port); 24 | writer.WriteBytes(ExternalPeerEndPoint.Address.GetAddressBytes()); 25 | writer.WriteInt32(ExternalPeerEndPoint.Port); 26 | } 27 | 28 | public override void ReadPayload(IValueReader reader) 29 | { 30 | base.ReadPayload(reader); 31 | var internalEndPointAddress = new IPAddress(reader.ReadBytes()); 32 | InternalOwnEndPoint = new IPEndPoint(internalEndPointAddress, reader.ReadInt32()); 33 | var externalEndPointAddress = new IPAddress(reader.ReadBytes()); 34 | ExternalPeerEndPoint = new IPEndPoint(externalEndPointAddress, reader.ReadInt32()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("IdealPeer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("IdealPeer")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fcfe1050-9512-4462-a589-357cdda4f1c8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TcpHolePunching/Peer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Peer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Peer")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5c295375-5321-4e7e-b272-a656a01bc56e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("IdealServer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("IdealServer")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b17e6e32-cb0b-4a5a-98d7-279885b815b2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Introducer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Introducer")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f843dc5d-3164-4902-90f2-75559cd3b1d0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/MessageBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TcpHolePunching.Messages 4 | { 5 | /* Credits to Eric Maupin (Tempest) */ 6 | public abstract class MessageBase 7 | { 8 | public MessageType MessageType { get; set; } 9 | 10 | public MessageBase(MessageType messageType) 11 | { 12 | MessageType = messageType; 13 | } 14 | 15 | /// 16 | /// Writes the message payload with . 17 | /// 18 | /// The writer to use for writing the payload. 19 | /// is null. 20 | public virtual void WritePayload(IValueWriter writer) 21 | { 22 | writer.WriteInt32((int) MessageType); 23 | } 24 | 25 | /// 26 | /// Reads the message payload with . 27 | /// 28 | /// The reader to use for reading the payload. 29 | /// is null. 30 | public virtual void ReadPayload(IValueReader reader) 31 | { 32 | MessageType = (MessageType) reader.ReadInt32(); 33 | } 34 | } 35 | 36 | public enum MessageType 37 | { 38 | Internal, 39 | RequestIntroducerRegistration, 40 | ResponseIntroducerRegistration, 41 | RequestIntroducerIntroduction, 42 | ResponseIntroducerIntroduction, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/Messages/ResponseIntroducerIntroductionMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching.Messages 8 | { 9 | public class ResponseIntroducerIntroductionMessage : MessageBase 10 | { 11 | /// 12 | /// This is the internal end point of the other peer. 13 | /// 14 | public IPEndPoint InternalPeerEndPoint { get; set; } 15 | /// 16 | /// This is the external end point of the other peer. 17 | /// 18 | public IPEndPoint ExternalPeerEndPoint { get; set; } 19 | 20 | public ResponseIntroducerIntroductionMessage() 21 | : base(MessageType.ResponseIntroducerIntroduction) 22 | { 23 | } 24 | 25 | public override void WritePayload(IValueWriter writer) 26 | { 27 | base.WritePayload(writer); 28 | writer.WriteBytes(InternalPeerEndPoint.Address.GetAddressBytes()); 29 | writer.WriteInt32(InternalPeerEndPoint.Port); 30 | writer.WriteBytes(ExternalPeerEndPoint.Address.GetAddressBytes()); 31 | writer.WriteInt32(ExternalPeerEndPoint.Port); 32 | } 33 | 34 | public override void ReadPayload(IValueReader reader) 35 | { 36 | base.ReadPayload(reader); 37 | var internalEndPointAddress = new IPAddress(reader.ReadBytes()); 38 | InternalPeerEndPoint = new IPEndPoint(internalEndPointAddress, reader.ReadInt32()); 39 | var externalEndPointAddress = new IPAddress(reader.ReadBytes()); 40 | ExternalPeerEndPoint = new IPEndPoint(externalEndPointAddress, reader.ReadInt32()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/ISerializable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // ISerializable.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | namespace TcpHolePunching 28 | { 29 | /// 30 | /// Contract representing a type that can serialize and deserialize itself. 31 | /// 32 | public interface ISerializable 33 | { 34 | /// 35 | /// Serializes the instance to the . 36 | /// 37 | /// The serialization context. 38 | /// The to serialize with. 39 | void Serialize (IValueWriter writer); 40 | 41 | /// 42 | /// Deserializes the instance from the . 43 | /// 44 | /// The serialization context. 45 | /// The to deserialize with. 46 | void Deserialize (IValueReader reader); 47 | } 48 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/NetworkPeer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace TcpHolePunching 11 | { 12 | public class NetworkPeer : NetworkClient 13 | { 14 | public Socket PeerSocket { get; private set; } 15 | public byte[] PeerBuffer { get; private set; } 16 | 17 | public event EventHandler OnConnectionAccepted; 18 | 19 | public NetworkPeer() : base() 20 | { 21 | PeerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 22 | Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 23 | Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 24 | PeerBuffer = new byte[1024]; 25 | } 26 | 27 | /// 28 | /// Only binds without listening. 29 | /// 30 | public void Bind(EndPoint on) 31 | { 32 | Socket.Bind(on); 33 | } 34 | 35 | public void Listen() 36 | { 37 | Socket.Listen(Int32.MaxValue); 38 | Task_BeginAccepting(); 39 | } 40 | 41 | private void Task_BeginAccepting() 42 | { 43 | var task = Task.Factory.FromAsync(Socket.BeginAccept, Socket.EndAccept, null); 44 | task.ContinueWith(nextTask => 45 | { 46 | Task_OnConnectionAccepted(task.Result); 47 | Task_BeginAccepting(); // Listen for another connection 48 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 49 | } 50 | 51 | private void Task_OnConnectionAccepted(Socket socket) 52 | { 53 | Console.WriteLine(String.Format("Connection to {0} accepted.", socket.RemoteEndPoint)); 54 | 55 | PeerSocket = socket; 56 | 57 | if (OnConnectionAccepted != null) 58 | OnConnectionAccepted(this, new ConnectionAcceptedEventArgs() { Socket = socket} ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using TcpHolePunching; 11 | using TcpHolePunching.Messages; 12 | 13 | namespace IdealServer 14 | { 15 | public class Program 16 | { 17 | private static NetworkPeer Incoming { get; set; } 18 | private static NetworkPeer Outgoing { get; set; } 19 | 20 | static void Main(string[] args) 21 | { 22 | Console.Title = "Ideal Server - TCP Hole Punching Proof of Concept"; 23 | 24 | Incoming = new NetworkPeer(); 25 | 26 | Incoming.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); 27 | Incoming.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 28 | 29 | Console.Write("Incoming: Bind to which port?: "); 30 | int portToBind = Int32.Parse(Console.ReadLine()); 31 | Incoming.Bind(new IPEndPoint(IPAddress.Any, portToBind)); 32 | Incoming.Listen(); 33 | 34 | Console.WriteLine(String.Format("Listening for clients on {0}...", Incoming.Socket.LocalEndPoint)); 35 | Console.ReadLine(); 36 | 37 | Outgoing = new NetworkPeer(); 38 | 39 | Outgoing.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); 40 | Outgoing.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 41 | 42 | Console.Write("Outgoing: Bind to which port?: "); 43 | Outgoing.Bind(new IPEndPoint(IPAddress.Any, portToBind)); 44 | Console.Write("Endpoint of your peer: "); 45 | 46 | var introducerEndpoint = Console.ReadLine().Parse(); 47 | 48 | Console.WriteLine(String.Format("Connecting to at {0}:{1}...", introducerEndpoint.Address, introducerEndpoint.Port)); 49 | Outgoing.Connect(introducerEndpoint.Address, introducerEndpoint.Port); 50 | 51 | Console.ReadLine(); 52 | 53 | Application.Run(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using TcpHolePunching; 11 | using TcpHolePunching.Messages; 12 | 13 | namespace IdealClient 14 | { 15 | public class Program 16 | { 17 | private static NetworkPeer Peer { get; set; } 18 | 19 | static void Main(string[] args) 20 | { 21 | Console.Title = "Ideal Client - TCP Hole Punching Proof of Concept"; 22 | 23 | Peer = new NetworkPeer(); 24 | Peer.OnConnectionAccepted += Peer_OnConnectionAccepted; 25 | Peer.OnConnectionSuccessful += PeerOnConnectionSuccessful; 26 | Peer.OnMessageSent += PeerOnMessageSent; 27 | Peer.OnMessageReceived += Peer_OnMessageReceived; 28 | 29 | Peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); 30 | Peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.IpTimeToLive, 1); 31 | Peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 32 | 33 | Console.Write("Bind to which port?: "); 34 | int portToBind = Int32.Parse(Console.ReadLine()); 35 | Peer.Bind(new IPEndPoint(IPAddress.Any, portToBind)); 36 | 37 | Console.Write("Endpoint of your peer: "); 38 | 39 | var introducerEndpoint = Console.ReadLine().Parse(); 40 | 41 | Console.WriteLine(String.Format("Connecting to at {0}:{1}...", introducerEndpoint.Address, introducerEndpoint.Port)); 42 | Peer.Connect(introducerEndpoint.Address, introducerEndpoint.Port); 43 | 44 | Console.Write("Press to set socket options back to normal."); 45 | Console.ReadLine(); 46 | Peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.IpTimeToLive, 4); 47 | 48 | Application.Run(); 49 | } 50 | 51 | static void Peer_OnConnectionAccepted(object sender, ConnectionAcceptedEventArgs e) 52 | { 53 | } 54 | 55 | static void PeerOnConnectionSuccessful(object sender, ConnectionAcceptedEventArgs e) 56 | { 57 | } 58 | 59 | static void PeerOnMessageSent(object sender, MessageSentEventArgs e) 60 | { 61 | } 62 | 63 | static void Peer_OnMessageReceived(object sender, MessageReceivedEventArgs e) 64 | { 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealPeer/IdealClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {CDF17D56-090A-4326-9CC4-57623A56E79A} 8 | Exe 9 | Properties 10 | IdealClient 11 | IdealClient 12 | v4.0 13 | 512 14 | 15 | 16 | x86 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {a1563fa0-04c7-4818-9d95-fe6383c62f22} 51 | TcpHolePunching 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /TcpHolePunching/IdealServer/IdealServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC} 8 | Exe 9 | Properties 10 | IdealServer 11 | IdealServer 12 | v4.0 13 | 512 14 | 15 | 16 | x86 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {a1563fa0-04c7-4818-9d95-fe6383c62f22} 51 | TcpHolePunching 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/Introducer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {B0C3DD03-FE40-4123-85D4-81E812A052D0} 9 | Exe 10 | Properties 11 | Introducer 12 | Introducer 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | {A1563FA0-04C7-4818-9D95-FE6383C62F22} 57 | TcpHolePunching 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /TcpHolePunching/Peer/Peer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD} 9 | Exe 10 | Properties 11 | Peer 12 | Peer 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Designer 53 | 54 | 55 | 56 | 57 | {A1563FA0-04C7-4818-9D95-FE6383C62F22} 58 | TcpHolePunching 59 | 60 | 61 | 62 | 69 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace TcpHolePunching 8 | { 9 | public static class StringExtensions 10 | { 11 | public static IPEndPoint Parse(this String str, int defaultPort = -1) 12 | { 13 | if (string.IsNullOrEmpty(str) 14 | || str.Trim().Length == 0) 15 | { 16 | throw new ArgumentException("Endpoint descriptor may not be empty."); 17 | } 18 | 19 | if (defaultPort != -1 && 20 | (defaultPort < IPEndPoint.MinPort 21 | || defaultPort > IPEndPoint.MaxPort)) 22 | { 23 | throw new ArgumentException(string.Format("Invalid default port '{0}'", defaultPort)); 24 | } 25 | 26 | string[] values = str.Split(new char[] { ':' }); 27 | IPAddress ipaddy; 28 | int port = -1; 29 | 30 | //check if we have an IPv6 or ports 31 | if (values.Length <= 2) // ipv4 or hostname 32 | { 33 | if (values.Length == 1) 34 | //no port is specified, default 35 | port = defaultPort; 36 | else 37 | port = AsPort(values[1]); 38 | 39 | //try to use the address as IPv4, otherwise get hostname 40 | if (!IPAddress.TryParse(values[0], out ipaddy)) 41 | ipaddy = IpToHost(values[0]); 42 | } 43 | else if (values.Length > 2) //ipv6 44 | { 45 | //could [a:b:c]:d 46 | if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]")) 47 | { 48 | string ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray()); 49 | ipaddy = IPAddress.Parse(ipaddressstring); 50 | port = AsPort(values[values.Length - 1]); 51 | } 52 | else //[a:b:c] or a:b:c 53 | { 54 | ipaddy = IPAddress.Parse(str); 55 | port = defaultPort; 56 | } 57 | } 58 | else 59 | { 60 | throw new FormatException(string.Format("Invalid endpoint ipaddress '{0}'", str)); 61 | } 62 | 63 | if (port == -1) 64 | throw new ArgumentException(string.Format("No port specified: '{0}'", str)); 65 | 66 | return new IPEndPoint(ipaddy, port); 67 | } 68 | 69 | private static int AsPort(this String str) 70 | { 71 | int port; 72 | 73 | if (!int.TryParse(str, out port) 74 | || port < IPEndPoint.MinPort 75 | || port > IPEndPoint.MaxPort) 76 | { 77 | throw new FormatException(string.Format("Invalid end point port '{0}'", str)); 78 | } 79 | 80 | return port; 81 | } 82 | 83 | private static IPAddress IpToHost(this String str) 84 | { 85 | var hosts = Dns.GetHostAddresses(str); 86 | 87 | if (hosts == null || hosts.Length == 0) 88 | throw new ArgumentException(string.Format("Host not found: {0}", str)); 89 | 90 | return hosts[0]; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/TcpHolePunching.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {A1563FA0-04C7-4818-9D95-FE6383C62F22} 9 | Library 10 | Properties 11 | TcpHolePunching 12 | TcpHolePunching 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | true 27 | 28 | 29 | x86 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | ..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll 43 | 44 | 45 | 46 | 47 | 48 | Designer 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 88 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/IValueReader.cs: -------------------------------------------------------------------------------- 1 | // 2 | // IValueReader.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2010 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.Linq; 29 | using System.Text; 30 | using TcpHolePunching.Messages; 31 | 32 | namespace TcpHolePunching 33 | { 34 | public interface IValueReader 35 | { 36 | /// 37 | /// Reads a boolean from the transport. 38 | /// 39 | bool ReadBool(); 40 | 41 | /// 42 | /// Reads an array of unsigned bytes from the transport. 43 | /// 44 | byte[] ReadBytes (); 45 | 46 | /// 47 | /// Reads the next bytes from the transport. 48 | /// 49 | /// The number of bytes to read. 50 | /// is < 0. 51 | byte[] ReadBytes (int count); 52 | 53 | /// 54 | /// Reads a signed byte (SByte) from the transport. 55 | /// 56 | SByte ReadSByte (); 57 | 58 | /// 59 | /// Reads a signed short (Int16) from the transport. 60 | /// 61 | Int16 ReadInt16 (); 62 | 63 | /// 64 | /// Reads a signed integer (Int32) from the transport. 65 | /// 66 | Int32 ReadInt32 (); 67 | 68 | /// 69 | /// Reads a signed long (Int64) from the transport. 70 | /// 71 | Int64 ReadInt64 (); 72 | 73 | /// 74 | /// Reads an unsigned byte (Byte) from the transport. 75 | /// 76 | Byte ReadByte (); 77 | 78 | /// 79 | /// Reads an unsigned integer (UInt16) from the transport. 80 | /// 81 | /// 82 | UInt16 ReadUInt16 (); 83 | 84 | /// 85 | /// Reads an unsigned integer (UInt32) from the transport. 86 | /// 87 | /// 88 | UInt32 ReadUInt32 (); 89 | 90 | /// 91 | /// Reads an unsigned long (UInt64) from the transport. 92 | /// 93 | UInt64 ReadUInt64 (); 94 | 95 | /// 96 | /// Reads a decimal from the transport. 97 | /// 98 | Decimal ReadDecimal (); 99 | 100 | /// 101 | /// Reads a single from the transport. 102 | /// 103 | Single ReadSingle(); 104 | 105 | /// 106 | /// Reads a double from the transport. 107 | /// 108 | Double ReadDouble(); 109 | 110 | /// 111 | /// Reads a string with from the transport. 112 | /// 113 | /// The encoding of the string. 114 | /// is null. 115 | string ReadString (Encoding encoding); 116 | 117 | /// 118 | /// Finalizes buffer. 119 | /// 120 | /// Connection providers should call this automatically when returns. 121 | void Flush(); 122 | } 123 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/StreamValueReader.cs: -------------------------------------------------------------------------------- 1 | // 2 | // StreamValueReader.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2010 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.IO; 29 | using System.Linq; 30 | using System.Text; 31 | 32 | namespace TcpHolePunching 33 | { 34 | public class StreamValueReader 35 | : IValueReader 36 | { 37 | private readonly Stream stream; 38 | 39 | public StreamValueReader (Stream stream) 40 | { 41 | if (stream == null) 42 | throw new ArgumentNullException ("stream"); 43 | if (!stream.CanRead) 44 | throw new ArgumentException ("Can not read from this stream", "stream"); 45 | 46 | this.stream = stream; 47 | } 48 | 49 | public bool ReadBool() 50 | { 51 | return (ReadByte() == 1); 52 | } 53 | 54 | public byte[] ReadBytes() 55 | { 56 | int count = ReadInt32(); 57 | return ReadBytes (count); 58 | } 59 | 60 | public byte[] ReadBytes (int count) 61 | { 62 | if (count < 0) 63 | throw new ArgumentOutOfRangeException ("count", "count must be >= 0"); 64 | 65 | byte[] buffer = new byte[count]; 66 | 67 | int i = 0; 68 | int bytes; 69 | while (i < buffer.Length && (bytes = this.stream.Read (buffer, i, count)) > 0) 70 | { 71 | i += bytes; 72 | count -= bytes; 73 | } 74 | 75 | return buffer; 76 | } 77 | 78 | public sbyte ReadSByte() 79 | { 80 | return (sbyte)this.stream.ReadByte(); 81 | } 82 | 83 | public short ReadInt16() 84 | { 85 | return BitConverter.ToInt16 (ReadBytes (sizeof (short)), 0); 86 | } 87 | 88 | public int ReadInt32() 89 | { 90 | return BitConverter.ToInt32 (ReadBytes (sizeof (int)), 0); 91 | } 92 | 93 | public long ReadInt64() 94 | { 95 | return BitConverter.ToInt64 (ReadBytes (sizeof (long)), 0); 96 | } 97 | 98 | public byte ReadByte() 99 | { 100 | return (byte)this.stream.ReadByte(); 101 | } 102 | 103 | public ushort ReadUInt16() 104 | { 105 | return BitConverter.ToUInt16 (ReadBytes (sizeof (ushort)), 0); 106 | } 107 | 108 | public uint ReadUInt32() 109 | { 110 | return BitConverter.ToUInt32 (ReadBytes (sizeof (uint)), 0); 111 | } 112 | 113 | public ulong ReadUInt64() 114 | { 115 | return BitConverter.ToUInt64 (ReadBytes (sizeof (ulong)), 0); 116 | } 117 | 118 | public string ReadString (Encoding encoding) 119 | { 120 | if (encoding == null) 121 | throw new ArgumentNullException ("encoding"); 122 | 123 | byte[] data = ReadBytes(); 124 | return (data.Length == 0) ? null : encoding.GetString (data, 0, data.Length); 125 | } 126 | 127 | public decimal ReadDecimal() 128 | { 129 | int len = ReadInt32(); 130 | int[] bits = new int[len]; 131 | for (int i = 0; i < bits.Length; ++i) 132 | bits[i] = ReadInt32(); 133 | 134 | return new decimal (bits); 135 | } 136 | 137 | public float ReadSingle() 138 | { 139 | return BitConverter.ToSingle (ReadBytes (sizeof (float)), 0); 140 | } 141 | 142 | public double ReadDouble() 143 | { 144 | return BitConverter.ToDouble (ReadBytes (sizeof (double)), 0); 145 | } 146 | 147 | public void Flush() 148 | { 149 | this.stream.Flush(); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/ISerializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // ISerializer.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | 29 | namespace TcpHolePunching 30 | { 31 | /// 32 | /// Contract for a type that serializes another type. 33 | /// 34 | public interface ISerializer 35 | { 36 | /// 37 | /// Serializes using . 38 | /// 39 | /// The serialization context. 40 | /// The writer to use to serialize. 41 | /// The element to serialize. 42 | void Serialize (IValueWriter writer, object element); 43 | 44 | /// 45 | /// Deserializes an element with . 46 | /// 47 | /// The serialization context. 48 | /// The reader to use to deserialize. 49 | /// The deserialized element. 50 | object Deserialize (IValueReader reader); 51 | } 52 | 53 | /// 54 | /// Contract for a type that serializes another type. 55 | /// 56 | /// The type to serialize and deserialize. 57 | public interface ISerializer 58 | { 59 | /// 60 | /// Serializes using . 61 | /// 62 | /// The serialization context. 63 | /// The writer to use to serialize. 64 | /// The element to serialize. 65 | void Serialize (IValueWriter writer, T element); 66 | 67 | /// 68 | /// Deserializes an element with . 69 | /// 70 | /// The serialization context. 71 | /// The reader to use to deserialize. 72 | /// The deserialized element. 73 | T Deserialize (IValueReader reader); 74 | } 75 | 76 | public static class Serializer 77 | { 78 | public static readonly ISerializer Default = new DefaultSerializer(); 79 | 80 | private class DefaultSerializer 81 | : ISerializer 82 | { 83 | public void Serialize (IValueWriter writer, T element) 84 | { 85 | Type etype; 86 | if (element != null) 87 | { 88 | etype = element.GetType(); 89 | if (etype.IsValueType && typeof (T) == typeof (object)) 90 | etype = typeof (object); 91 | } 92 | else 93 | etype = typeof (object); 94 | 95 | ObjectSerializer.GetSerializer (etype).Serialize (writer, element); 96 | } 97 | 98 | public T Deserialize (IValueReader reader) 99 | { 100 | return (T)ObjectSerializer.GetSerializer (typeof (T)).Deserialize (reader); 101 | } 102 | } 103 | } 104 | 105 | internal class FixedSerializer 106 | : ISerializer 107 | { 108 | public FixedSerializer (Type type) 109 | { 110 | if (type == null) 111 | throw new ArgumentNullException ("type"); 112 | 113 | this.serializer = ObjectSerializer.GetSerializer (type); 114 | } 115 | 116 | public void Serialize (IValueWriter writer, object element) 117 | { 118 | this.serializer.Serialize (writer, element); 119 | } 120 | 121 | public object Deserialize (IValueReader reader) 122 | { 123 | return this.serializer.Deserialize (reader); 124 | } 125 | 126 | private readonly ObjectSerializer serializer; 127 | } 128 | } -------------------------------------------------------------------------------- /TcpHolePunching/Introducer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using TcpHolePunching; 7 | using TcpHolePunching.Messages; 8 | 9 | namespace Introducer 10 | { 11 | class Program 12 | { 13 | private static NetworkIntroducer Introducer { get; set; } 14 | 15 | static void Main(string[] args) 16 | { 17 | Console.Title = "Introducer - TCP Hole Punching Proof of Concept"; 18 | 19 | Introducer = new NetworkIntroducer(); 20 | Introducer.OnConnectionAccepted += Introducer_OnConnectionAccepted; 21 | Introducer.OnMessageSent += Introducer_OnMessageSent; 22 | Introducer.OnMessageReceived += Introducer_OnMessageReceived; 23 | 24 | Introducer.Listen(new IPEndPoint(IPAddress.Any, 1618)); 25 | Console.WriteLine(String.Format("Listening for clients on {0}...", Introducer.Socket.LocalEndPoint)); 26 | 27 | Console.ReadLine(); 28 | } 29 | 30 | static void Introducer_OnConnectionAccepted(object sender, ConnectionAcceptedEventArgs e) 31 | { 32 | } 33 | 34 | static void Introducer_OnMessageSent(object sender, MessageSentEventArgs e) 35 | { 36 | } 37 | 38 | static void Introducer_OnMessageReceived(object sender, MessageReceivedEventArgs e) 39 | { 40 | switch (e.MessageType) 41 | { 42 | case MessageType.RequestIntroducerRegistration: 43 | { 44 | var message = new RequestIntroducerRegistrationMessage(); 45 | message.ReadPayload(e.MessageReader); 46 | 47 | // A client wants to register 48 | // Get his internal endpoint 49 | var internalEndPoint = message.InternalClientEndPoint; 50 | // Get his external endpoint 51 | var externalEndPoint = e.From; 52 | 53 | Introducer.Registrants.Add(new Registrant() 54 | { 55 | InternalEndPoint = internalEndPoint, 56 | ExternalEndPoint = externalEndPoint 57 | }); 58 | 59 | Introducer.Send(e.From, new ResponseIntroducerRegistrationMessage() 60 | { 61 | RegisteredEndPoint = e.From 62 | }); 63 | } 64 | break; 65 | case MessageType.RequestIntroducerIntroduction: 66 | { 67 | var message = new RequestIntroducerIntroductionMessage(); 68 | message.ReadPayload(e.MessageReader); 69 | 70 | // A client, A, wants to be introduced to another peer, B 71 | var bExternalEndPoint = message.ExternalPeerEndPoint; 72 | 73 | // Get this peer's registration 74 | var b = 75 | Introducer.Registrants.First( 76 | registrant => registrant.ExternalEndPoint.Equals(message.ExternalPeerEndPoint)); 77 | 78 | var a = new Registrant() 79 | {InternalEndPoint = message.InternalOwnEndPoint, ExternalEndPoint = e.From}; 80 | 81 | Introducer.Send(a.ExternalEndPoint, new ResponseIntroducerIntroductionMessage() 82 | { 83 | InternalPeerEndPoint = b.InternalEndPoint, 84 | ExternalPeerEndPoint = b.ExternalEndPoint, 85 | }); 86 | 87 | Introducer.Send(b.ExternalEndPoint, new ResponseIntroducerIntroductionMessage() 88 | { 89 | InternalPeerEndPoint = a.InternalEndPoint, 90 | ExternalPeerEndPoint = a.ExternalEndPoint, 91 | }); 92 | } 93 | break; 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/StreamValueWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // StreamValueWriter.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.IO; 29 | using System.Linq; 30 | using System.Text; 31 | 32 | namespace TcpHolePunching 33 | { 34 | public class StreamValueWriter 35 | : IValueWriter 36 | { 37 | private readonly Stream stream; 38 | 39 | public StreamValueWriter (Stream stream) 40 | { 41 | if (stream == null) 42 | throw new ArgumentNullException ("stream"); 43 | if (!stream.CanWrite) 44 | throw new ArgumentException ("Can not write to this stream", "stream"); 45 | if (!BitConverter.IsLittleEndian) // TODO: Support. 46 | throw new NotSupportedException ("Big Endian architecture not supported"); 47 | 48 | this.stream = stream; 49 | } 50 | 51 | public void WriteByte (byte value) 52 | { 53 | this.stream.WriteByte (value); 54 | } 55 | 56 | public void WriteSByte (sbyte value) 57 | { 58 | this.stream.WriteByte ((byte)value); 59 | } 60 | 61 | public bool WriteBool (bool value) 62 | { 63 | this.stream.WriteByte ((byte)((value) ? 1 : 0)); 64 | 65 | return value; 66 | } 67 | 68 | public void WriteBytes (byte[] value) 69 | { 70 | if (value == null) 71 | throw new ArgumentNullException ("value"); 72 | 73 | WriteInt32 (value.Length); 74 | this.stream.Write (value, 0, value.Length); 75 | } 76 | 77 | public void WriteBytes (byte[] value, int offset, int length) 78 | { 79 | if (value == null) 80 | throw new ArgumentNullException ("value"); 81 | if (offset < 0 || offset >= value.Length) 82 | throw new ArgumentOutOfRangeException ("offset", "offset can not negative or >=data.Length"); 83 | if (length < 0 || offset + length >= value.Length) 84 | throw new ArgumentOutOfRangeException ("length", "length can not be negative or combined with offset longer than the array"); 85 | 86 | WriteInt32 (length); 87 | this.stream.Write (value, offset, length); 88 | } 89 | 90 | public void WriteInt16 (short value) 91 | { 92 | Write (BitConverter.GetBytes (value)); 93 | } 94 | 95 | public void WriteInt32 (int value) 96 | { 97 | Write (BitConverter.GetBytes (value)); 98 | } 99 | 100 | public void WriteInt64 (long value) 101 | { 102 | Write (BitConverter.GetBytes (value)); 103 | } 104 | 105 | public void WriteUInt16 (ushort value) 106 | { 107 | Write (BitConverter.GetBytes (value)); 108 | } 109 | 110 | public void WriteUInt32 (uint value) 111 | { 112 | Write (BitConverter.GetBytes (value)); 113 | } 114 | 115 | public void WriteUInt64 (ulong value) 116 | { 117 | Write (BitConverter.GetBytes (value)); 118 | } 119 | 120 | public void WriteDecimal (decimal value) 121 | { 122 | int[] bits = Decimal.GetBits (value); 123 | WriteInt32 (bits.Length); 124 | for (int i = 0; i < bits.Length; ++i) 125 | WriteInt32 (bits[i]); 126 | } 127 | 128 | public void WriteSingle (float value) 129 | { 130 | Write (BitConverter.GetBytes (value)); 131 | } 132 | 133 | public void WriteDouble (double value) 134 | { 135 | Write (BitConverter.GetBytes (value)); 136 | } 137 | 138 | public void WriteString (Encoding encoding, string value) 139 | { 140 | if (encoding == null) 141 | throw new ArgumentNullException ("encoding"); 142 | 143 | WriteBytes (!String.IsNullOrEmpty (value) ? encoding.GetBytes (value) : new byte[0]); 144 | } 145 | 146 | public void Flush() 147 | { 148 | this.stream.Flush(); 149 | } 150 | 151 | private void Write (byte[] data) 152 | { 153 | this.stream.Write (data, 0, data.Length); 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/IValueWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // IValueWriter.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2010 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace TcpHolePunching 32 | { 33 | /// 34 | /// Serialization contract. 35 | /// 36 | public interface IValueWriter 37 | { 38 | /// 39 | /// Writes an unsigned byte to the transport. 40 | /// 41 | /// 42 | void WriteByte (byte value); 43 | 44 | /// 45 | /// Writes a signed byte to the transport. 46 | /// 47 | /// The value to write. 48 | void WriteSByte (sbyte value); 49 | 50 | /// 51 | /// Writes a boolean to the transport. 52 | /// 53 | /// The value to write. 54 | /// 55 | bool WriteBool (bool value); 56 | 57 | /// 58 | /// Writes an array of unsigned bytes to the transport. 59 | /// 60 | /// The value to write. 61 | /// is null. 62 | void WriteBytes (byte[] value); 63 | 64 | /// 65 | /// Writes an array segment to the transport. 66 | /// 67 | /// The array to write from. 68 | /// The offset of to start writing from. 69 | /// The length to write from in . 70 | /// 71 | void WriteBytes (byte[] value, int offset, int length); 72 | 73 | /// 74 | /// Writes a signed short (Int16) to the transport. 75 | /// 76 | /// The value to write. 77 | void WriteInt16 (Int16 value); 78 | 79 | /// 80 | /// Writes a signed integer (Int32) to the transport. 81 | /// 82 | /// The value to write. 83 | void WriteInt32 (Int32 value); 84 | 85 | /// 86 | /// Writes a signed long (Int64) to the transport. 87 | /// 88 | /// The value to write. 89 | void WriteInt64 (Int64 value); 90 | 91 | /// 92 | /// Writes an unsigned short to the transport. 93 | /// 94 | /// The value to write. 95 | void WriteUInt16 (UInt16 value); 96 | 97 | /// 98 | /// Writes an unsigned integer to the transport. 99 | /// 100 | /// The value to write. 101 | void WriteUInt32 (UInt32 value); 102 | 103 | /// 104 | /// Writes an unsigned long to the transport. 105 | /// 106 | /// The value to write. 107 | void WriteUInt64 (UInt64 value); 108 | 109 | /// 110 | /// Writes a decimal to the transport. 111 | /// 112 | /// The value to write 113 | void WriteDecimal (Decimal value); 114 | 115 | /// 116 | /// Writes a single to the transport. 117 | /// 118 | /// The value to write 119 | void WriteSingle (Single value); 120 | 121 | /// 122 | /// Writes a double to the transport. 123 | /// 124 | /// The value to write 125 | void WriteDouble (Double value); 126 | 127 | /// 128 | /// Writes a string with to the transport. 129 | /// 130 | /// The encoding to use. 131 | /// The value to write. 132 | void WriteString (Encoding encoding, string value); 133 | 134 | /// 135 | /// Flushes any buffered data to the transport. 136 | /// 137 | void Flush(); 138 | } 139 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Introducer", "Introducer\Introducer.csproj", "{B0C3DD03-FE40-4123-85D4-81E812A052D0}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Peer", "Peer\Peer.csproj", "{505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpHolePunching", "TcpHolePunching\TcpHolePunching.csproj", "{A1563FA0-04C7-4818-9D95-FE6383C62F22}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdealClient", "IdealPeer\IdealClient.csproj", "{CDF17D56-090A-4326-9CC4-57623A56E79A}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdealServer", "IdealServer\IdealServer.csproj", "{C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|Mixed Platforms = Debug|Mixed Platforms 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|Mixed Platforms = Release|Mixed Platforms 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Debug|Any CPU.ActiveCfg = Debug|x86 25 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 26 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Debug|Mixed Platforms.Build.0 = Debug|x86 27 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Debug|x86.ActiveCfg = Debug|x86 28 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Debug|x86.Build.0 = Debug|x86 29 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Release|Any CPU.ActiveCfg = Release|x86 30 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Release|Mixed Platforms.ActiveCfg = Release|x86 31 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Release|Mixed Platforms.Build.0 = Release|x86 32 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Release|x86.ActiveCfg = Release|x86 33 | {B0C3DD03-FE40-4123-85D4-81E812A052D0}.Release|x86.Build.0 = Release|x86 34 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Debug|Any CPU.ActiveCfg = Debug|x86 35 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 36 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Debug|Mixed Platforms.Build.0 = Debug|x86 37 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Debug|x86.ActiveCfg = Debug|x86 38 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Debug|x86.Build.0 = Debug|x86 39 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Release|Any CPU.ActiveCfg = Release|x86 40 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Release|Mixed Platforms.ActiveCfg = Release|x86 41 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Release|Mixed Platforms.Build.0 = Release|x86 42 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Release|x86.ActiveCfg = Release|x86 43 | {505A5ACD-FACD-4B76-9D9D-C40F3EB33DDD}.Release|x86.Build.0 = Release|x86 44 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Debug|Any CPU.ActiveCfg = Debug|x86 45 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 46 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Debug|Mixed Platforms.Build.0 = Debug|x86 47 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Debug|x86.ActiveCfg = Debug|x86 48 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Debug|x86.Build.0 = Debug|x86 49 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Release|Any CPU.ActiveCfg = Release|x86 50 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Release|Mixed Platforms.ActiveCfg = Release|x86 51 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Release|Mixed Platforms.Build.0 = Release|x86 52 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Release|x86.ActiveCfg = Release|x86 53 | {A1563FA0-04C7-4818-9D95-FE6383C62F22}.Release|x86.Build.0 = Release|x86 54 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 57 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 58 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Debug|x86.ActiveCfg = Debug|Any CPU 59 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 62 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Release|Mixed Platforms.Build.0 = Release|Any CPU 63 | {CDF17D56-090A-4326-9CC4-57623A56E79A}.Release|x86.ActiveCfg = Release|Any CPU 64 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 67 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 68 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Debug|x86.ActiveCfg = Debug|Any CPU 69 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 72 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Release|Mixed Platforms.Build.0 = Release|Any CPU 73 | {C71EDA02-7098-479E-9B4E-3DD0A2C83FFC}.Release|x86.ActiveCfg = Release|Any CPU 74 | EndGlobalSection 75 | GlobalSection(SolutionProperties) = preSolution 76 | HideSolutionNode = FALSE 77 | EndGlobalSection 78 | EndGlobal 79 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/BufferValueReader.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BufferValueReader.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011-2012 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.IO; 29 | using System.Text; 30 | using Buff = System.Buffer; 31 | 32 | namespace TcpHolePunching 33 | { 34 | public class BufferValueReader 35 | : IValueReader 36 | { 37 | private readonly byte[] buffer; 38 | private readonly int length; 39 | 40 | public BufferValueReader (byte[] buffer) 41 | { 42 | if (buffer == null) 43 | throw new ArgumentNullException ("buffer"); 44 | 45 | this.buffer = buffer; 46 | this.length = buffer.Length; 47 | } 48 | 49 | public BufferValueReader (byte[] buffer, int offset, int length) 50 | { 51 | if (buffer == null) 52 | throw new ArgumentNullException ("buffer"); 53 | 54 | this.buffer = buffer; 55 | this.position = offset; 56 | this.length = length; 57 | } 58 | 59 | /// 60 | /// Gets the underlying buffer. 61 | /// 62 | public byte[] Buffer 63 | { 64 | get { return this.buffer; } 65 | } 66 | 67 | /// 68 | /// Gets or sets the position of the reader in the buffer. 69 | /// 70 | public int Position 71 | { 72 | get { return this.position; } 73 | set { this.position = value; } 74 | } 75 | 76 | public bool ReadBool() 77 | { 78 | return (this.buffer[this.position++] == 1); 79 | } 80 | 81 | public byte[] ReadBytes() 82 | { 83 | int len = ReadInt32(); 84 | 85 | byte[] b = new byte[len]; 86 | Buff.BlockCopy (this.buffer, this.Position, b, 0, len); 87 | this.Position += len; 88 | 89 | return b; 90 | } 91 | 92 | public byte[] ReadBytes (int count) 93 | { 94 | if (count < 0) 95 | throw new ArgumentOutOfRangeException ("count", "count must be >= 0"); 96 | 97 | byte[] b = new byte[count]; 98 | Buff.BlockCopy (this.buffer, this.position, b, 0, count); 99 | this.position += count; 100 | 101 | return b; 102 | } 103 | 104 | public sbyte ReadSByte() 105 | { 106 | return (sbyte)this.buffer[this.position++]; 107 | } 108 | 109 | public short ReadInt16() 110 | { 111 | short v = BitConverter.ToInt16 (this.buffer, this.position); 112 | this.position += sizeof (short); 113 | 114 | return v; 115 | } 116 | 117 | public int ReadInt32() 118 | { 119 | int v = BitConverter.ToInt32 (this.buffer, this.position); 120 | this.position += sizeof (int); 121 | 122 | return v; 123 | } 124 | 125 | public long ReadInt64() 126 | { 127 | long v = BitConverter.ToInt64 (this.buffer, this.position); 128 | this.position += sizeof (long); 129 | 130 | return v; 131 | } 132 | 133 | public byte ReadByte() 134 | { 135 | return this.buffer[this.position++]; 136 | } 137 | 138 | public ushort ReadUInt16() 139 | { 140 | ushort v = BitConverter.ToUInt16 (this.buffer, this.position); 141 | this.position += sizeof (ushort); 142 | 143 | return v; 144 | } 145 | 146 | public uint ReadUInt32() 147 | { 148 | uint v = BitConverter.ToUInt32 (this.buffer, this.position); 149 | this.position += sizeof (uint); 150 | 151 | return v; 152 | } 153 | 154 | public ulong ReadUInt64() 155 | { 156 | ulong v = BitConverter.ToUInt64 (this.buffer, this.position); 157 | this.position += sizeof (ulong); 158 | 159 | return v; 160 | } 161 | 162 | public decimal ReadDecimal() 163 | { 164 | int[] parts = new int[4]; 165 | for (int i = 0; i < parts.Length; ++i) 166 | parts[i] = ReadInt32 (); 167 | 168 | return new decimal (parts); 169 | } 170 | 171 | public float ReadSingle() 172 | { 173 | float v = BitConverter.ToSingle (this.buffer, this.position); 174 | this.position += sizeof (float); 175 | 176 | return v; 177 | } 178 | 179 | public double ReadDouble () 180 | { 181 | double v = BitConverter.ToDouble (this.buffer, this.position); 182 | this.position += sizeof (double); 183 | 184 | return v; 185 | } 186 | 187 | public string ReadString (Encoding encoding) 188 | { 189 | if (encoding == null) 190 | throw new ArgumentNullException ("encoding"); 191 | 192 | int len = Read7BitEncodedInt(); 193 | if (len == -1) 194 | return null; 195 | 196 | string v = encoding.GetString (this.buffer, this.position, len); 197 | this.Position += len; 198 | 199 | return v; 200 | } 201 | 202 | public void Flush() 203 | { 204 | } 205 | 206 | private int position; 207 | 208 | private int Read7BitEncodedInt() 209 | { 210 | int count = 0; 211 | int shift = 0; 212 | byte b; 213 | 214 | do 215 | { 216 | b = ReadByte(); 217 | 218 | count |= (b & 127) << shift; 219 | shift += 7; 220 | } while ((b & 128) != 0); 221 | 222 | return count; 223 | } 224 | } 225 | } -------------------------------------------------------------------------------- /TcpHolePunching/Peer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using TcpHolePunching; 11 | using TcpHolePunching.Messages; 12 | 13 | namespace Peer 14 | { 15 | public class Program 16 | { 17 | private static NetworkPeer IntroducerSocket { get; set; } 18 | private static NetworkPeer ListenSocket { get; set; } 19 | private static NetworkPeer ConnectSocketInternal { get; set; } 20 | private static NetworkPeer ConnectSocketExternal { get; set; } 21 | 22 | private static int PORT = 53472; 23 | 24 | static void Main(string[] args) 25 | { 26 | Console.Title = "Peer - TCP Hole Punching Proof of Concept"; 27 | 28 | ListenSocket = new NetworkPeer(); 29 | ListenSocket.OnConnectionAccepted += (s, e1) => Console.WriteLine("ListenSocket.OnConnectionAccepted"); 30 | ListenSocket.Bind(new IPEndPoint(IPAddress.Any, PORT)); 31 | ListenSocket.Listen(); 32 | Console.WriteLine(String.Format("Listening for clients on {0}...", ListenSocket.Socket.LocalEndPoint)); 33 | 34 | IntroducerSocket = new NetworkPeer(); 35 | IntroducerSocket.OnConnectionAccepted += Peer_OnConnectionAccepted; 36 | IntroducerSocket.OnConnectionSuccessful += PeerOnConnectionSuccessful; 37 | IntroducerSocket.OnMessageSent += PeerOnMessageSent; 38 | IntroducerSocket.OnMessageReceived += Peer_OnMessageReceived; 39 | 40 | IntroducerSocket.Bind(new IPEndPoint(IPAddress.Any, PORT)); 41 | 42 | Console.Write("Endpoint of the introducer (try 50.18.245.235:1618): "); 43 | 44 | var input = Console.ReadLine(); 45 | input = (String.IsNullOrEmpty(input)) ? "50.18.245.235:1618" : input; 46 | var introducerEndpoint = input.Parse(); 47 | 48 | Console.WriteLine(String.Format("Connecting to the Introducer at {0}:{1}...", introducerEndpoint.Address, introducerEndpoint.Port)); 49 | IntroducerSocket.Connect(introducerEndpoint.Address, introducerEndpoint.Port); 50 | 51 | Application.Run(); 52 | } 53 | 54 | static void Peer_OnConnectionAccepted(object sender, ConnectionAcceptedEventArgs e) 55 | { 56 | Console.WriteLine(); 57 | } 58 | 59 | static void PeerOnConnectionSuccessful(object sender, ConnectionAcceptedEventArgs e) 60 | { 61 | Console.WriteLine(); 62 | Console.WriteLine("Requesting to register with the Introducer..."); 63 | IntroducerSocket.Send(new RequestIntroducerRegistrationMessage() { InternalClientEndPoint = (IPEndPoint) e.Socket.LocalEndPoint} ); 64 | } 65 | 66 | static void PeerOnMessageSent(object sender, MessageSentEventArgs e) 67 | { 68 | } 69 | 70 | static void Peer_OnMessageReceived(object sender, MessageReceivedEventArgs e) 71 | { 72 | switch (e.MessageType) 73 | { 74 | case MessageType.ResponseIntroducerRegistration: 75 | { 76 | var message = new ResponseIntroducerRegistrationMessage(); 77 | message.ReadPayload(e.MessageReader); 78 | 79 | Console.WriteLine(String.Format("Introducer: You have been registered as \"{0}\".", message.RegisteredEndPoint)); 80 | 81 | Console.Write("Endpoint of your peer: "); 82 | 83 | var peerEndPoint = Console.ReadLine().Parse(); 84 | 85 | Console.WriteLine(String.Format("Requesting an introduction to {0}:{1}...", peerEndPoint.Address, peerEndPoint.Port)); 86 | IntroducerSocket.Send(new RequestIntroducerIntroductionMessage() { InternalOwnEndPoint = (IPEndPoint) IntroducerSocket.Socket.LocalEndPoint, ExternalPeerEndPoint = peerEndPoint} ); 87 | } 88 | break; 89 | case MessageType.ResponseIntroducerIntroduction: 90 | { 91 | var message = new ResponseIntroducerIntroductionMessage(); 92 | message.ReadPayload(e.MessageReader); 93 | 94 | Console.WriteLine(String.Format("Introducer: Your peer's internal endpoint is \"{0}\".", message.InternalPeerEndPoint)); 95 | Console.WriteLine(String.Format("Introducer: Your peer's external endpoint is \"{0}\".", message.ExternalPeerEndPoint)); 96 | 97 | ConnectSocketInternal = new NetworkPeer(); 98 | ConnectSocketInternal.Bind(new IPEndPoint(IPAddress.Any, PORT)); 99 | Console.WriteLine(String.Format("Connecting to your peer's internal endpoint...")); 100 | ConnectSocketInternal.OnConnectionSuccessful += (s, e1) => Console.WriteLine("ConnectSocketInternal.OnConnectionSuccessful"); 101 | ConnectSocketInternal.Connect(message.InternalPeerEndPoint.Address, message.InternalPeerEndPoint.Port); 102 | 103 | ConnectSocketExternal = new NetworkPeer(); 104 | ConnectSocketExternal.Bind(new IPEndPoint(IPAddress.Any, PORT)); 105 | Console.WriteLine(String.Format("Connecting to your peer's external endpoint...")); 106 | ConnectSocketExternal.OnConnectionSuccessful += (s, e1) => Console.WriteLine("ConnectSocketExternal.OnConnectionSuccessful"); 107 | ConnectSocketExternal.Connect(message.ExternalPeerEndPoint.Address, message.ExternalPeerEndPoint.Port); 108 | } 109 | break; 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching.6.0.ReSharper.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Never 39 | 40 | 41 | 42 | 43 | 44 | False 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/NetworkClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using TcpHolePunching.Messages; 10 | 11 | namespace TcpHolePunching 12 | { 13 | /// 14 | /// A specialized Socket for the Introducer. 15 | /// 16 | public class NetworkClient 17 | { 18 | /// 19 | /// Gets the underlying raw socket. 20 | /// 21 | public Socket Socket { get; private set; } 22 | /// 23 | /// The buffer in which to receive bytes from the transport. 24 | /// 25 | public byte[] Buffer { get; set; } 26 | 27 | /// 28 | /// Occurs after an accepted client has been registered. 29 | /// 30 | public event EventHandler OnConnectionSuccessful; 31 | public event EventHandler OnMessageSent; 32 | public event EventHandler OnMessageReceived; 33 | 34 | public NetworkClient() 35 | { 36 | Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 37 | // Set our special Tcp hole punching socket options 38 | Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 39 | Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 40 | Buffer = new byte[1024]; 41 | } 42 | 43 | /// 44 | /// 45 | public void Connect(IPAddress host, int port) 46 | { 47 | Task_BeginConnecting(host, port); 48 | } 49 | 50 | private void Task_BeginConnecting(IPAddress host, int port) 51 | { 52 | var task = Task.Factory.FromAsync(Socket.BeginConnect(host, port, null, null), Socket.EndConnect); 53 | task.ContinueWith(nextTask => Task_OnConnectSuccessful(), TaskContinuationOptions.OnlyOnRanToCompletion); 54 | } 55 | 56 | private void Task_OnConnectSuccessful() 57 | { 58 | Console.WriteLine(String.Format("Connected to {0}.", Socket.RemoteEndPoint)); 59 | 60 | Task_BeginReceive(); 61 | 62 | // Invoke the event 63 | if (OnConnectionSuccessful != null) 64 | OnConnectionSuccessful(this, new ConnectionAcceptedEventArgs() { Socket = Socket }); 65 | } 66 | 67 | public void Send(MessageBase messageBase) 68 | { 69 | // If the registrant exists 70 | if (Socket.Connected) 71 | { 72 | var data = messageBase.GetBytes(); 73 | var task = Task.Factory.FromAsync(Socket.BeginSend(data, 0, data.Length, SocketFlags.None, null, Socket), Socket.EndSend); 74 | task.ContinueWith(nextTask => Task_OnSendCompleted(task.Result, data.Length, Socket.RemoteEndPoint, messageBase.MessageType), TaskContinuationOptions.OnlyOnRanToCompletion); 75 | } 76 | } 77 | 78 | private void Task_OnSendCompleted(int numBytesSent, int expectedBytesSent, EndPoint to, MessageType messageType) 79 | { 80 | if (numBytesSent != expectedBytesSent) 81 | Console.WriteLine(String.Format("Warning: Expected to send {0} bytes but actually sent {1}!", 82 | expectedBytesSent, numBytesSent)); 83 | 84 | Console.WriteLine(String.Format("Sent a {0} byte {1}Message to {2}.", numBytesSent, messageType, to)); 85 | 86 | if (OnMessageSent != null) 87 | OnMessageSent(this, new MessageSentEventArgs() {Length = numBytesSent, To = to}); 88 | } 89 | 90 | private void Task_BeginReceive() 91 | { 92 | var task = Task.Factory.FromAsync(Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, null, null), Socket.EndReceive); 93 | task.ContinueWith(nextTask => 94 | { 95 | try 96 | { 97 | Task_OnReceiveCompleted(task.Result); 98 | Task_BeginReceive(); // Receive more data 99 | } 100 | catch (Exception ex) 101 | { 102 | var exceptionMessage = (ex.InnerException != null) ? ex.InnerException.Message : ex.Message; 103 | Console.WriteLine(exceptionMessage); 104 | ShutdownAndClose(); 105 | } 106 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 107 | } 108 | 109 | private void Task_OnReceiveCompleted(int numBytesRead) 110 | { 111 | // Build back our MessageReader 112 | var reader = new BufferValueReader(Buffer); 113 | var message = new Message(); 114 | message.ReadPayload(reader); 115 | reader.Position = 0; 116 | 117 | Console.WriteLine(String.Format("Received a {0} byte {1}Message from {2}.", numBytesRead, message.MessageType, Socket.RemoteEndPoint)); 118 | 119 | if (OnMessageReceived != null) 120 | OnMessageReceived(this, new MessageReceivedEventArgs() { From = (IPEndPoint) Socket.RemoteEndPoint, MessageReader = reader, MessageType = message.MessageType }); 121 | } 122 | 123 | /// 124 | /// Only disconnects without shutting down. 125 | /// 126 | public void Disconnect() 127 | { 128 | Socket.Disconnect(true); 129 | } 130 | 131 | /// 132 | /// Explicitly stops sending and receiving, and then closes the socket. 133 | /// 134 | public void ShutdownAndClose() 135 | { 136 | Console.WriteLine("Shutting down socket..."); 137 | 138 | try 139 | { 140 | Socket.Shutdown(SocketShutdown.Both); 141 | Socket.Close(); 142 | } 143 | catch 144 | { 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/NetworkIntroducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using TcpHolePunching.Messages; 10 | 11 | namespace TcpHolePunching 12 | { 13 | /// 14 | /// A specialized Socket for the Introducer. 15 | /// 16 | public class NetworkIntroducer 17 | { 18 | /// 19 | /// Gets the underlying raw socket. 20 | /// 21 | public Socket Socket { get; private set; } 22 | /// 23 | /// Gets the list of connected clients. 24 | /// 25 | public List Clients { get; private set; } 26 | /// 27 | /// Gets the list of connected clients that have registered with RequestIntroductionRegistrationMessage. 28 | /// 29 | public List Registrants { get; private set; } 30 | 31 | /// 32 | /// Occurs after an accepted client has been registered. 33 | /// 34 | public event EventHandler OnConnectionAccepted; 35 | public event EventHandler OnMessageSent; 36 | public event EventHandler OnMessageReceived; 37 | 38 | public NetworkIntroducer() 39 | { 40 | Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 41 | Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 42 | Clients = new List(); 43 | Registrants = new List(); 44 | } 45 | 46 | /// 47 | /// Asynchronously accepts a client. Upon accepting the client, the client will automatically be added to 48 | /// a list of registered clients. 49 | /// 50 | public void Listen(EndPoint on) 51 | { 52 | Socket.Bind(on); 53 | Socket.Listen(Int32.MaxValue); 54 | Task_BeginAccepting(); 55 | } 56 | 57 | private void Task_BeginAccepting() 58 | { 59 | var task = Task.Factory.FromAsync(Socket.BeginAccept, Socket.EndAccept, null); 60 | task.ContinueWith(nextTask => 61 | { 62 | Task_OnConnectionAccepted(task.Result); 63 | Task_BeginAccepting(); // Listen for another connection 64 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 65 | } 66 | 67 | private void Task_OnConnectionAccepted(Socket socket) 68 | { 69 | Console.WriteLine(String.Format("Connection to {0} accepted.", socket.RemoteEndPoint)); 70 | 71 | // If the registrant was already registered 72 | if (Clients.FindAll(registrant => registrant.RemoteEndPoint == socket.RemoteEndPoint).Any()) 73 | { 74 | // Remove the registrant 75 | Clients.RemoveAll(registrant => registrant.RemoteEndPoint == socket.RemoteEndPoint); 76 | } 77 | 78 | // Register the registrant 79 | var newRegistrant = new Client(socket); 80 | Clients.Add(newRegistrant); 81 | 82 | Task_BeginReceive(newRegistrant); 83 | 84 | // Invoke the event 85 | if (OnConnectionAccepted != null) 86 | OnConnectionAccepted(this, new ConnectionAcceptedEventArgs() { Socket = socket } ); 87 | } 88 | 89 | public void Send(EndPoint to, MessageBase messageBase) 90 | { 91 | var registrant = Clients.Find(r => r.RemoteEndPoint == to); 92 | 93 | // If the registrant exists 94 | if (registrant != null && registrant.Socket.Connected) 95 | { 96 | var data = messageBase.GetBytes(); 97 | var task = Task.Factory.FromAsync(registrant.Socket.BeginSend(data, 0, data.Length, SocketFlags.None, null, Socket), registrant.Socket.EndSend); 98 | task.ContinueWith(nextTask => Task_OnSendCompleted(task.Result, data.Length, registrant.RemoteEndPoint, messageBase.MessageType), TaskContinuationOptions.OnlyOnRanToCompletion); 99 | } 100 | } 101 | 102 | private void Task_OnSendCompleted(int numBytesSent, int expectedBytesSent, EndPoint to, MessageType messageType) 103 | { 104 | if (numBytesSent != expectedBytesSent) 105 | Console.WriteLine(String.Format("Warning: Expected to send {0} bytes but actually sent {1}!", 106 | expectedBytesSent, numBytesSent)); 107 | 108 | Console.WriteLine(String.Format("Sent a {0} byte {1}Message to {2}.", numBytesSent, messageType, to)); 109 | 110 | if (OnMessageSent != null) 111 | OnMessageSent(this, new MessageSentEventArgs() {Length = numBytesSent, To = to}); 112 | } 113 | 114 | private void Task_BeginReceive(Client registrant) 115 | { 116 | var task = Task.Factory.FromAsync(registrant.Socket.BeginReceive(registrant.Buffer, 0, registrant.Buffer.Length, SocketFlags.None, null, null), registrant.Socket.EndReceive); 117 | task.ContinueWith(nextTask => 118 | { 119 | try 120 | { 121 | Task_OnReceiveCompleted(task.Result, registrant); 122 | Task_BeginReceive(registrant); // Receive more data 123 | } 124 | catch (Exception ex) 125 | { 126 | var exceptionMessage = (ex.InnerException != null) ? ex.InnerException.Message : ex.Message; 127 | Console.WriteLine(exceptionMessage); 128 | ShutdownAndClose(); 129 | } 130 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 131 | } 132 | 133 | private void Task_OnReceiveCompleted(int numBytesRead, Client registrant) 134 | { 135 | // Build back our MessageReader 136 | var reader = new BufferValueReader(registrant.Buffer); 137 | var message = new Message(); 138 | message.ReadPayload(reader); 139 | reader.Position = 0; 140 | 141 | Console.WriteLine(String.Format("Received a {0} byte {1}Message from {2}.", numBytesRead, message.MessageType, registrant.Socket.RemoteEndPoint)); 142 | 143 | if (OnMessageReceived != null) 144 | OnMessageReceived(this, new MessageReceivedEventArgs() { From = (IPEndPoint) registrant.RemoteEndPoint, MessageReader = reader, MessageType = message.MessageType }); 145 | } 146 | 147 | /// 148 | /// Explicitly stops sending and receiving, and then closes the socket. 149 | /// 150 | public void ShutdownAndClose() 151 | { 152 | Console.WriteLine("Shutting down sockets..."); 153 | 154 | try 155 | { 156 | Socket.Shutdown(SocketShutdown.Both); 157 | Socket.Close(); 158 | } 159 | catch 160 | { 161 | } 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/SerializerExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // SerializerExtensions.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.Linq; 29 | using System.Text; 30 | using System.Collections.Generic; 31 | 32 | #if NET_4 33 | using System.Collections.Concurrent; 34 | #endif 35 | 36 | namespace TcpHolePunching 37 | { 38 | public static class SerializerExtensions 39 | { 40 | public static void WriteUniversalDate (this IValueWriter writer, DateTime date) 41 | { 42 | if (writer == null) 43 | throw new ArgumentNullException ("writer"); 44 | 45 | if (date.Kind != DateTimeKind.Utc) 46 | date = date.ToUniversalTime(); 47 | 48 | writer.WriteInt64 (date.Ticks); 49 | } 50 | 51 | public static DateTime ReadUniversalDate (this IValueReader reader) 52 | { 53 | if (reader == null) 54 | throw new ArgumentNullException ("reader"); 55 | 56 | return new DateTime (reader.ReadInt64(), DateTimeKind.Utc); 57 | } 58 | 59 | /// 60 | /// Writes a date value. 61 | /// 62 | public static void WriteDate (this IValueWriter writer, DateTime date) 63 | { 64 | #if !SILVERLIGHT && !WINDOWS_PHONE 65 | WriteLocalDate (writer, date); 66 | #else 67 | writer.WriteInt64 (date.Ticks); 68 | #endif 69 | } 70 | 71 | /// 72 | /// Reads a date value. 73 | /// 74 | public static DateTime ReadDate (this IValueReader reader) 75 | { 76 | #if !SILVERLIGHT && !WINDOWS_PHONE 77 | return ReadLocalDate (reader).Item2; 78 | #else 79 | return new DateTime (reader.ReadInt64(), DateTimeKind.Unspecified); 80 | #endif 81 | } 82 | 83 | #if !SILVERLIGHT && !WINDOWS_PHONE 84 | public static void WriteLocalDate (this IValueWriter writer, DateTime date) 85 | { 86 | WriteLocalDate (writer, date, TimeZoneInfo.Local); 87 | } 88 | 89 | public static void WriteLocalDate (this IValueWriter writer, DateTime date, TimeZoneInfo timeZone) 90 | { 91 | if (writer == null) 92 | throw new ArgumentNullException ("writer"); 93 | 94 | writer.WriteString (timeZone.ToSerializedString()); 95 | writer.WriteInt64 (date.ToLocalTime().Ticks); 96 | } 97 | 98 | public static Tuple ReadLocalDate (this IValueReader reader) 99 | { 100 | if (reader == null) 101 | throw new ArgumentNullException ("reader"); 102 | 103 | return new Tuple (TimeZoneInfo.FromSerializedString (reader.ReadString()), 104 | new DateTime (reader.ReadInt64(), DateTimeKind.Unspecified)); 105 | } 106 | #endif 107 | 108 | public static void WriteString (this IValueWriter writer, string value) 109 | { 110 | if (writer == null) 111 | throw new ArgumentNullException ("writer"); 112 | 113 | writer.WriteString (Encoding.UTF8, value); 114 | } 115 | 116 | public static string ReadString (this IValueReader reader) 117 | { 118 | if (reader == null) 119 | throw new ArgumentNullException ("reader"); 120 | 121 | return reader.ReadString (Encoding.UTF8); 122 | } 123 | 124 | public static void WriteEnumerable (this IValueWriter writer, IEnumerable enumerable) 125 | where T : ISerializable 126 | { 127 | if (writer == null) 128 | throw new ArgumentNullException ("writer"); 129 | if (enumerable == null) 130 | throw new ArgumentNullException ("enumerable"); 131 | 132 | T[] elements = enumerable.ToArray(); 133 | writer.WriteInt32 (elements.Length); 134 | for (int i = 0; i < elements.Length; ++i) 135 | elements[i].Serialize (writer); 136 | } 137 | 138 | public static void WriteEnumerable (this IValueWriter writer, ISerializer serializer, IEnumerable enumerable) 139 | { 140 | if (writer == null) 141 | throw new ArgumentNullException ("writer"); 142 | if (serializer == null) 143 | throw new ArgumentNullException ("serializer"); 144 | if (enumerable == null) 145 | throw new ArgumentNullException ("enumerable"); 146 | 147 | T[] elements = enumerable.ToArray(); 148 | writer.WriteInt32 (elements.Length); 149 | for (int i = 0; i < elements.Length; ++i) 150 | serializer.Serialize (writer, elements[i]); 151 | } 152 | 153 | public static IEnumerable ReadEnumerable (this IValueReader reader, Func elementFactory) 154 | where T : ISerializable 155 | { 156 | if (reader == null) 157 | throw new ArgumentNullException ("reader"); 158 | if (elementFactory == null) 159 | throw new ArgumentNullException ("elementFactory"); 160 | 161 | int length = reader.ReadInt32(); 162 | T[] elements = new T[length]; 163 | for (int i = 0; i < elements.Length; ++i) 164 | (elements[i] = elementFactory()).Deserialize (reader); 165 | 166 | return elements; 167 | } 168 | 169 | public static IEnumerable ReadEnumerable (this IValueReader reader, ISerializer serializer) 170 | { 171 | if (reader == null) 172 | throw new ArgumentNullException ("reader"); 173 | if (serializer == null) 174 | throw new ArgumentNullException ("serializer"); 175 | 176 | int length = reader.ReadInt32(); 177 | T[] elements = new T[length]; 178 | for (int i = 0; i < elements.Length; ++i) 179 | elements[i] = serializer.Deserialize (reader); 180 | 181 | return elements; 182 | } 183 | 184 | public static IEnumerable ReadEnumerable (this IValueReader reader, Func elementFactory) 185 | { 186 | if (reader == null) 187 | throw new ArgumentNullException ("reader"); 188 | if (elementFactory == null) 189 | throw new ArgumentNullException ("elementFactory"); 190 | 191 | int length = reader.ReadInt32(); 192 | T[] elements = new T[length]; 193 | for (int i = 0; i < elements.Length; ++i) 194 | elements[i] = elementFactory (reader); 195 | 196 | return elements; 197 | } 198 | 199 | public static void Write (this IValueWriter writer, object element, Type serializeAs) 200 | { 201 | Write (writer, element, new FixedSerializer (serializeAs)); 202 | } 203 | 204 | public static void Write (this IValueWriter writer, object element, ISerializer serializer) 205 | { 206 | if (writer == null) 207 | throw new ArgumentNullException ("writer"); 208 | if (element == null) 209 | throw new ArgumentNullException ("element"); 210 | if (serializer == null) 211 | throw new ArgumentNullException ("serializer"); 212 | 213 | serializer.Serialize (writer, element); 214 | } 215 | 216 | public static void Write (this IValueWriter writer, T element) 217 | { 218 | Write (writer, element, Serializer.Default); 219 | } 220 | 221 | public static void Write (this IValueWriter writer, T element, ISerializer serializer) 222 | { 223 | if (writer == null) 224 | throw new ArgumentNullException ("writer"); 225 | if (serializer == null) 226 | throw new ArgumentNullException ("serializer"); 227 | 228 | serializer.Serialize (writer, element); 229 | } 230 | 231 | public static T Read (this IValueReader reader) 232 | { 233 | if (reader == null) 234 | throw new ArgumentNullException ("reader"); 235 | 236 | return Read (reader, Serializer.Default); 237 | } 238 | 239 | public static T Read (this IValueReader reader, ISerializer serializer) 240 | { 241 | if (reader == null) 242 | throw new ArgumentNullException ("reader"); 243 | if (serializer == null) 244 | throw new ArgumentNullException ("serializer"); 245 | 246 | return serializer.Deserialize (reader); 247 | } 248 | 249 | public static object Read (this IValueReader reader) 250 | { 251 | return Read (reader); 252 | } 253 | 254 | public static object Read (this IValueReader reader, Type type) 255 | { 256 | if (reader == null) 257 | throw new ArgumentNullException ("reader"); 258 | 259 | return ObjectSerializer.GetSerializer (type).Deserialize (reader); 260 | } 261 | } 262 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/ObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectSerializer.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011-2012 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.Collections.Generic; 29 | using System.Diagnostics; 30 | using System.IO; 31 | using System.Linq; 32 | using System.Reflection; 33 | using System.Runtime.Serialization; 34 | using System.Text; 35 | 36 | #if NET_4 37 | using System.Collections.Concurrent; 38 | #endif 39 | 40 | #if !SILVERLIGHT 41 | using System.Runtime.Serialization.Formatters.Binary; 42 | #endif 43 | 44 | #if !SAFE 45 | using System.Reflection.Emit; 46 | #endif 47 | 48 | namespace TcpHolePunching 49 | { 50 | internal class ObjectSerializer 51 | { 52 | private readonly Type type; 53 | 54 | public ObjectSerializer (Type type) 55 | { 56 | if (type == null) 57 | throw new ArgumentNullException ("type"); 58 | 59 | this.type = type; 60 | 61 | GenerateSerialization(); 62 | } 63 | 64 | public void Serialize (IValueWriter writer, object obj) 65 | { 66 | if (writer == null) 67 | throw new ArgumentNullException ("writer"); 68 | 69 | serializer (writer, obj, false); 70 | } 71 | 72 | public T Deserialize (IValueReader reader) 73 | { 74 | if (typeof(T) != this.type) 75 | throw new ArgumentException ("Type does not match serializer type"); 76 | 77 | return (T)Deserialize (reader); 78 | } 79 | 80 | public object Deserialize (IValueReader reader) 81 | { 82 | if (reader == null) 83 | throw new ArgumentNullException ("reader"); 84 | 85 | return deserializer (reader, false); 86 | } 87 | 88 | private Func deserializer; 89 | private Action serializer; 90 | 91 | private void GenerateSerialization() 92 | { 93 | deserializer = GetDeserializer (this.type, this); 94 | serializer = GetSerializer(); 95 | } 96 | 97 | private class SerializationPair 98 | { 99 | public readonly Func Deserializer; 100 | public readonly Action Serializer; 101 | 102 | public SerializationPair (Func des, Action ser) 103 | { 104 | Deserializer = des; 105 | Serializer = ser; 106 | } 107 | } 108 | 109 | private ConstructorInfo ctor; 110 | private Dictionary members; 111 | private bool deserializingConstructor; 112 | 113 | private static Func GetDeserializer (Type t, ObjectSerializer oserializer) 114 | { 115 | if (t.IsPrimitive) 116 | { 117 | if (t == typeof(bool)) 118 | return (r, sh) => r.ReadBool(); 119 | if (t == typeof(byte)) 120 | return (r, sh) => r.ReadByte(); 121 | if (t == typeof(sbyte)) 122 | return (r, sh) => r.ReadSByte(); 123 | if (t == typeof(short)) 124 | return (r, sh) => r.ReadInt16(); 125 | if (t == typeof(ushort)) 126 | return (r, sh) => r.ReadUInt16(); 127 | if (t == typeof(int)) 128 | return (r, sh) => r.ReadInt32 (); 129 | if (t == typeof(uint)) 130 | return (r, sh) => r.ReadUInt32(); 131 | if (t == typeof(long)) 132 | return (r, sh) => r.ReadInt64(); 133 | if (t == typeof(ulong)) 134 | return (r, sh) => r.ReadUInt64(); 135 | if (t == typeof(float)) 136 | return (r, sh) => r.ReadSingle(); 137 | if (t == typeof(double)) 138 | return (r, sh) => r.ReadDouble(); 139 | 140 | throw new ArgumentOutOfRangeException ("type"); // Shouldn't happen. 141 | } 142 | else if (t == typeof(decimal)) 143 | return (r, sh) => r.ReadDecimal (); 144 | else if (t == typeof(DateTime)) 145 | return (r, sh) => r.ReadDate(); 146 | else if (t == typeof(string)) 147 | return (r, sh) => !r.ReadBool() ? null : r.ReadString (Encoding.UTF8); 148 | else if (t.IsEnum) 149 | { 150 | Type btype = Enum.GetUnderlyingType (t); 151 | return GetDeserializer (btype, oserializer); 152 | } 153 | else if (t.IsArray || t == typeof(Array)) 154 | { 155 | Type etype = t.GetElementType(); 156 | 157 | return (r, sh) => 158 | { 159 | if (!r.ReadBool()) 160 | return null; 161 | 162 | Array a = Array.CreateInstance (etype, r.ReadInt32()); 163 | for (int i = 0; i < a.Length; ++i) 164 | a.SetValue (r.Read (etype), i); 165 | 166 | return a; 167 | }; 168 | } 169 | else 170 | { 171 | return (r, skipHeader) => 172 | { 173 | if (!skipHeader) 174 | { 175 | ushort objectHeader = r.ReadUInt16(); 176 | if ((objectHeader & 1) == 0) 177 | return null; 178 | } 179 | 180 | object value; 181 | 182 | if (typeof(ISerializable).IsAssignableFrom (t)) 183 | { 184 | oserializer.LoadCtor (t); 185 | if (!oserializer.deserializingConstructor) 186 | { 187 | value = oserializer.ctor.Invoke (null); 188 | ((ISerializable)value).Deserialize (r); 189 | } 190 | else 191 | value = oserializer.ctor.Invoke (new object[] { r }); 192 | 193 | return value; 194 | } 195 | 196 | #if !SILVERLIGHT 197 | if (t.GetCustomAttributes (true).OfType().Any ()) 198 | return oserializer.SerializableDeserializer (r); 199 | #endif 200 | 201 | throw new ArgumentException ("No serializer found for type " + t); 202 | 203 | //oserializer.LoadMembers (t); 204 | 205 | //value = oserializer.ctor.Invoke (null); 206 | 207 | //foreach (var kvp in oserializer.members) 208 | //{ 209 | // object mvalue = kvp.Value.Deserializer (r, false); 210 | // if (kvp.Key.MemberType == MemberTypes.Field) 211 | // ((FieldInfo)kvp.Key).SetValue (value, mvalue); 212 | // else if (kvp.Key.MemberType == MemberTypes.Property) 213 | // ((PropertyInfo)kvp.Key).SetValue (value, mvalue, null); 214 | //} 215 | 216 | //return value; 217 | }; 218 | } 219 | } 220 | 221 | private Action GetSerializer() 222 | { 223 | return GetSerializerAction (this.type); 224 | } 225 | 226 | private Action GetSerializerAction (Type t) 227 | { 228 | if (t.IsPrimitive) 229 | { 230 | if (t == typeof (bool)) 231 | return (w, v, sh) => w.WriteBool ((bool)v); 232 | if (t == typeof (byte)) 233 | return (w, v, sh) => w.WriteByte ((byte)v); 234 | else if (t == typeof (sbyte)) 235 | return (w, v, sh) => w.WriteSByte ((sbyte)v); 236 | else if (t == typeof (short)) 237 | return (w, v, sh) => w.WriteInt16 ((short)v); 238 | else if (t == typeof (ushort)) 239 | return (w, v, sh) => w.WriteUInt16 ((ushort)v); 240 | else if (t == typeof (int)) 241 | return (w, v, sh) => w.WriteInt32 ((int)v); 242 | else if (t == typeof (uint)) 243 | return (w, v, sh) => w.WriteUInt32 ((uint)v); 244 | else if (t == typeof (long)) 245 | return (w, v, sh) => w.WriteInt64 ((long)v); 246 | else if (t == typeof (ulong)) 247 | return (w, v, sh) => w.WriteUInt64 ((ulong)v); 248 | else if (t == typeof (float)) 249 | return (w, v, sh) => w.WriteSingle ((float)v); 250 | else if (t == typeof (double)) 251 | return (w, v, sh) => w.WriteDouble ((double)v); 252 | 253 | throw new ArgumentOutOfRangeException ("type"); // Shouldn't happen. 254 | } 255 | else if (t == typeof (decimal)) 256 | return (w, v, sh) => w.WriteDecimal ((decimal)v); 257 | else if (t == typeof (DateTime)) 258 | return (w, v, sh) => w.WriteDate ((DateTime)(object)v); 259 | else if (t == typeof (string)) 260 | { 261 | return (w, v, sh) => 262 | { 263 | w.WriteBool (v != null); 264 | if (v != null) 265 | w.WriteString (Encoding.UTF8, (string)v); 266 | }; 267 | } 268 | else if (t.IsEnum) 269 | { 270 | Type btype = Enum.GetUnderlyingType (t); 271 | return GetSerializerAction (btype); 272 | } 273 | else if (t.IsArray || t == typeof(Array)) 274 | { 275 | return (w, v, sh) => 276 | { 277 | if (v == null) 278 | { 279 | w.WriteBool (false); 280 | return; 281 | } 282 | 283 | w.WriteBool (true); 284 | 285 | Array a = (Array)v; 286 | w.WriteInt32 (a.Length); 287 | 288 | if (t.IsPrimitive) 289 | { 290 | for (int i = 0; i < a.Length; ++i) 291 | w.Write (a.GetValue (i)); 292 | } 293 | else 294 | { 295 | var etype = t.GetElementType(); 296 | for (int i = 0; i < a.Length; ++i) 297 | w.Write (a.GetValue (i), etype); 298 | } 299 | }; 300 | } 301 | else 302 | { 303 | return (w, v, sh) => 304 | { 305 | if (!sh) 306 | { 307 | ushort objectHeader = 0; 308 | //if (v == null) 309 | //{ 310 | // w.WriteBool (false); 311 | // return; 312 | //} 313 | 314 | Type actualType = null; 315 | if (v != null) 316 | { 317 | actualType = v.GetType(); 318 | 319 | objectHeader <<= 1; 320 | objectHeader |= 1; 321 | } 322 | 323 | w.WriteUInt16 (objectHeader); 324 | if (v == null) 325 | return; 326 | 327 | //w.WriteBool (true); 328 | //w.WriteString (String.Format ("{0}, {1}", actualType.FullName, actualType.Assembly.GetName().Name)); 329 | 330 | if (!t.IsAssignableFrom (actualType)) 331 | throw new ArgumentException(); 332 | 333 | if (actualType != t) 334 | { 335 | GetSerializer (actualType).serializer (w, v, true); 336 | return; 337 | } 338 | } 339 | 340 | var serializable = (v as ISerializable); 341 | if (serializable != null) 342 | { 343 | serializable.Serialize (w); 344 | return; 345 | } 346 | 347 | #if !SILVERLIGHT 348 | if (t != typeof(object) && t.GetCustomAttributes (true).OfType().Any ()) 349 | { 350 | SerializableSerializer (w, v); 351 | return; 352 | } 353 | #endif 354 | 355 | throw new ArgumentException ("No serializer found or specified for type " + t, "value"); 356 | 357 | //LoadMembers (t); 358 | 359 | //var props = this.members; 360 | 361 | //foreach (var kvp in props) 362 | //{ 363 | // if (kvp.Key.MemberType == MemberTypes.Field) 364 | // kvp.Value.Serializer (w, ((FieldInfo)kvp.Key).GetValue (v), false); 365 | // else if (kvp.Key.MemberType == MemberTypes.Property) 366 | // kvp.Value.Serializer (w, ((PropertyInfo)kvp.Key).GetValue (v, null), false); 367 | //} 368 | }; 369 | } 370 | } 371 | 372 | private void LoadMembers (Type t) 373 | { 374 | LoadCtor (t); 375 | 376 | if (this.members != null) 377 | return; 378 | 379 | this.members = t.GetMembers (BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.NonPublic) 380 | .Where (mi => 381 | { 382 | if (mi.MemberType == MemberTypes.Field) 383 | { 384 | var fi = (FieldInfo)mi; 385 | if (typeof(Delegate).IsAssignableFrom (fi.FieldType.BaseType)) 386 | return false; 387 | 388 | return !fi.IsInitOnly; 389 | } 390 | else if (mi.MemberType == MemberTypes.Property) 391 | { 392 | var p = (PropertyInfo)mi; 393 | return (p.GetSetMethod() != null && p.GetIndexParameters().Length == 0); 394 | } 395 | 396 | return false; 397 | }) 398 | .ToDictionary (mi => mi, mi => 399 | { 400 | ObjectSerializer os = null; 401 | if (mi.MemberType == MemberTypes.Field) 402 | os = GetSerializerInternal (((FieldInfo)mi).FieldType); 403 | else if (mi.MemberType == MemberTypes.Property) 404 | os = GetSerializerInternal (((PropertyInfo)mi).PropertyType); 405 | 406 | return new SerializationPair (os.Deserialize, os.Serialize); 407 | }); 408 | } 409 | 410 | private void LoadCtor (Type t) 411 | { 412 | if (this.ctor != null) 413 | return; 414 | 415 | this.ctor = t.GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new[] {typeof (IValueReader) }, null) 416 | ?? t.GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Type.EmptyTypes, null); 417 | 418 | if (this.ctor == null) 419 | throw new ArgumentException ("No empty or (ISerializationContext,IValueReader) constructor found for " + t.Name); 420 | 421 | this.deserializingConstructor = this.ctor.GetParameters().Length == 2; 422 | } 423 | 424 | #if !SILVERLIGHT 425 | private object SerializableDeserializer (IValueReader reader) 426 | { 427 | bool isNull = false; 428 | if (this.type.IsClass) 429 | isNull = reader.ReadBool(); 430 | 431 | if (isNull) 432 | return null; 433 | 434 | byte[] data = reader.ReadBytes(); 435 | using (MemoryStream stream = new MemoryStream (data)) 436 | return new BinaryFormatter().Deserialize (stream, null); 437 | } 438 | 439 | private void SerializableSerializer (IValueWriter writer, object value) 440 | { 441 | if (this.type.IsClass) 442 | writer.WriteBool (value == null); 443 | 444 | using (MemoryStream stream = new MemoryStream()) 445 | { 446 | new BinaryFormatter().Serialize (stream, value); 447 | writer.WriteBytes (stream.ToArray()); 448 | } 449 | } 450 | #endif 451 | 452 | private void Serialize (IValueWriter writer, object value, bool skipHeader) 453 | { 454 | this.serializer (writer, value, skipHeader); 455 | } 456 | 457 | private object Deserialize (IValueReader reader, bool skipHeader) 458 | { 459 | return this.deserializer (reader, skipHeader); 460 | } 461 | 462 | #if NET_4 463 | private static readonly ConcurrentDictionary Serializers = new ConcurrentDictionary(); 464 | #else 465 | private static readonly Dictionary Serializers = new Dictionary (); 466 | #endif 467 | 468 | internal ObjectSerializer GetSerializerInternal (Type stype) 469 | { 470 | if (this.type == stype) 471 | return this; 472 | 473 | return GetSerializer (stype); 474 | } 475 | 476 | private static readonly ObjectSerializer baseSerializer = new ObjectSerializer (typeof(object)); 477 | 478 | internal static ObjectSerializer GetSerializer (Type type) 479 | { 480 | if (type == typeof(object) || type.IsInterface || type.IsAbstract) 481 | return baseSerializer; 482 | 483 | ObjectSerializer serializer; 484 | #if NET_4 485 | serializer = Serializers.GetOrAdd (type, t => new ObjectSerializer (t)); 486 | #else 487 | bool exists; 488 | lock (Serializers) 489 | exists = Serializers.TryGetValue (type, out serializer); 490 | 491 | if (!exists) 492 | { 493 | serializer = new ObjectSerializer (type); 494 | lock (Serializers) 495 | { 496 | if (!Serializers.ContainsKey (type)) 497 | Serializers.Add (type, serializer); 498 | } 499 | } 500 | #endif 501 | 502 | return serializer; 503 | } 504 | } 505 | } -------------------------------------------------------------------------------- /TcpHolePunching/TcpHolePunching/BufferValueWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BufferValueWriter.cs 3 | // 4 | // Author: 5 | // Eric Maupin 6 | // 7 | // Copyright (c) 2011-2012 Eric Maupin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | using System; 28 | using System.IO; 29 | using System.Text; 30 | using Buff = System.Buffer; 31 | 32 | namespace TcpHolePunching 33 | { 34 | #if SAFE 35 | public class BufferValueWriter 36 | #else 37 | public unsafe class BufferValueWriter 38 | #endif 39 | : IValueWriter 40 | { 41 | public BufferValueWriter (byte[] buffer) 42 | { 43 | if (buffer == null) 44 | throw new ArgumentNullException ("buffer"); 45 | 46 | this.buffer = buffer; 47 | } 48 | 49 | public int Length 50 | { 51 | get { return this.position; } 52 | set 53 | { 54 | if (value > this.position) 55 | { 56 | int additionalCapacity = value - this.position; 57 | if (this.position + additionalCapacity >= this.buffer.Length) 58 | { 59 | int curLength = this.buffer.Length; 60 | int newLength = curLength * 2; 61 | while (newLength <= curLength + additionalCapacity) 62 | newLength *= 2; 63 | 64 | byte[] newbuffer = new byte[newLength]; 65 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 66 | this.buffer = newbuffer; 67 | } 68 | } 69 | 70 | this.position = value; 71 | } 72 | } 73 | 74 | public byte[] Buffer 75 | { 76 | get { return this.buffer; } 77 | } 78 | 79 | public void WriteByte (byte value) 80 | { 81 | if (this.position + 1 >= this.buffer.Length) 82 | { 83 | int curLength = this.buffer.Length; 84 | int newLength = curLength * 2; 85 | while (newLength <= curLength + 1) 86 | newLength *= 2; 87 | 88 | byte[] newbuffer = new byte[newLength]; 89 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 90 | this.buffer = newbuffer; 91 | } 92 | 93 | this.buffer[this.position++] = value; 94 | } 95 | 96 | public void WriteSByte (sbyte value) 97 | { 98 | if (this.position + 1 >= this.buffer.Length) 99 | { 100 | int curLength = this.buffer.Length; 101 | int newLength = curLength * 2; 102 | while (newLength <= curLength + 1) 103 | newLength *= 2; 104 | 105 | byte[] newbuffer = new byte[newLength]; 106 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 107 | this.buffer = newbuffer; 108 | } 109 | 110 | this.buffer[this.position++] = (byte)value; 111 | } 112 | 113 | public bool WriteBool (bool value) 114 | { 115 | if (this.position + 1 >= this.buffer.Length) 116 | { 117 | int curLength = this.buffer.Length; 118 | int newLength = curLength * 2; 119 | while (newLength <= curLength + 1) 120 | newLength *= 2; 121 | 122 | byte[] newbuffer = new byte[newLength]; 123 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 124 | this.buffer = newbuffer; 125 | } 126 | 127 | this.buffer[this.position++] = (byte)((value) ? 1 : 0); 128 | 129 | return value; 130 | } 131 | 132 | public void WriteBytes (byte[] value) 133 | { 134 | if (value == null) 135 | throw new ArgumentNullException ("value"); 136 | 137 | int additionalCapacity = sizeof(int) + value.Length; 138 | if (this.position + additionalCapacity >= this.buffer.Length) 139 | { 140 | int curLength = this.buffer.Length; 141 | int newLength = curLength * 2; 142 | while (newLength <= curLength + additionalCapacity) 143 | newLength *= 2; 144 | 145 | byte[] newbuffer = new byte[newLength]; 146 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 147 | this.buffer = newbuffer; 148 | } 149 | 150 | Buff.BlockCopy (BitConverter.GetBytes (value.Length), 0, this.buffer, this.position, sizeof(int)); 151 | this.position += sizeof (int); 152 | Buff.BlockCopy (value, 0, this.buffer, this.position, value.Length); 153 | this.position += value.Length; 154 | } 155 | 156 | public void WriteBytes (byte[] value, int offset, int length) 157 | { 158 | if (value == null) 159 | throw new ArgumentNullException ("value"); 160 | if (offset < 0 || offset >= value.Length) 161 | throw new ArgumentOutOfRangeException ("offset", "offset can not negative or >=data.Length"); 162 | if (length < 0 || offset + length > value.Length) 163 | throw new ArgumentOutOfRangeException ("length", "length can not be negative or combined with offset longer than the array"); 164 | 165 | int additionalCapacity = sizeof(int) + length; 166 | if (this.position + additionalCapacity >= this.buffer.Length) 167 | { 168 | int curLength = this.buffer.Length; 169 | int newLength = curLength * 2; 170 | while (newLength <= curLength + additionalCapacity) 171 | newLength *= 2; 172 | 173 | byte[] newbuffer = new byte[newLength]; 174 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 175 | this.buffer = newbuffer; 176 | } 177 | 178 | Buff.BlockCopy (BitConverter.GetBytes (length), 0, this.buffer, this.position, sizeof (int)); 179 | this.position += sizeof (int); 180 | Buff.BlockCopy (value, offset, this.buffer, this.position, length); 181 | this.position += length; 182 | } 183 | 184 | public void InsertBytes (int offset, byte[] value, int valueOffset, int length) 185 | { 186 | if (value == null) 187 | throw new ArgumentNullException ("value"); 188 | if (valueOffset < 0 || valueOffset >= value.Length) 189 | throw new ArgumentOutOfRangeException ("offset", "offset can not negative or >=data.Length"); 190 | if (length < 0 || valueOffset + length > value.Length) 191 | throw new ArgumentOutOfRangeException ("length", "length can not be negative or combined with offset longer than the array"); 192 | 193 | if (this.position + length >= this.buffer.Length) 194 | { 195 | int curLength = this.buffer.Length; 196 | int newLength = curLength * 2; 197 | while (newLength <= curLength + length) 198 | newLength *= 2; 199 | 200 | byte[] newbuffer = new byte[newLength]; 201 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 202 | this.buffer = newbuffer; 203 | } 204 | 205 | if (offset != this.position) 206 | Buff.BlockCopy (this.buffer, offset, this.buffer, offset + length, this.position - offset); 207 | 208 | Buff.BlockCopy (value, valueOffset, this.buffer, offset, length); 209 | this.position += length; 210 | } 211 | 212 | public void WriteInt16 (short value) 213 | { 214 | if (this.position + sizeof (short) >= this.buffer.Length) 215 | { 216 | int curLength = this.buffer.Length; 217 | int newLength = curLength * 2; 218 | while (newLength <= curLength + sizeof (short)) 219 | newLength *= 2; 220 | 221 | byte[] newbuffer = new byte[newLength]; 222 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 223 | this.buffer = newbuffer; 224 | } 225 | 226 | #if SAFE 227 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof(short)); 228 | #else 229 | fixed (byte* ub = this.buffer) 230 | *((short*) (ub + this.position)) = value; 231 | #endif 232 | 233 | this.position += sizeof (short); 234 | } 235 | 236 | public void WriteInt32 (int value) 237 | { 238 | if (this.position + sizeof (int) >= this.buffer.Length) 239 | { 240 | int curLength = this.buffer.Length; 241 | int newLength = curLength * 2; 242 | while (newLength <= curLength + sizeof (int)) 243 | newLength *= 2; 244 | 245 | byte[] newbuffer = new byte[newLength]; 246 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 247 | this.buffer = newbuffer; 248 | } 249 | 250 | #if SAFE 251 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (int)); 252 | #else 253 | fixed (byte* ub = this.buffer) 254 | *((int*) (ub + this.position)) = value; 255 | #endif 256 | 257 | this.position += sizeof (int); 258 | } 259 | 260 | public void WriteInt64 (long value) 261 | { 262 | if (this.position + sizeof (long) >= this.buffer.Length) 263 | { 264 | int curLength = this.buffer.Length; 265 | int newLength = curLength * 2; 266 | while (newLength <= curLength + sizeof (long)) 267 | newLength *= 2; 268 | 269 | byte[] newbuffer = new byte[newLength]; 270 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 271 | this.buffer = newbuffer; 272 | } 273 | 274 | #if SAFE 275 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (long)); 276 | #else 277 | fixed (byte* ub = this.buffer) 278 | *((long*) (ub + this.position)) = value; 279 | #endif 280 | 281 | this.position += sizeof (long); 282 | } 283 | 284 | public void WriteUInt16 (ushort value) 285 | { 286 | if (this.position + sizeof (ushort) >= this.buffer.Length) 287 | { 288 | int curLength = this.buffer.Length; 289 | int newLength = curLength * 2; 290 | while (newLength <= curLength + sizeof (ushort)) 291 | newLength *= 2; 292 | 293 | byte[] newbuffer = new byte[newLength]; 294 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 295 | this.buffer = newbuffer; 296 | } 297 | 298 | #if SAFE 299 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (ushort)); 300 | #else 301 | fixed (byte* ub = this.buffer) 302 | *((ushort*) (ub + this.position)) = value; 303 | #endif 304 | 305 | this.position += sizeof (ushort); 306 | } 307 | 308 | public void WriteUInt32 (uint value) 309 | { 310 | if (this.position + sizeof (uint) >= this.buffer.Length) 311 | { 312 | int curLength = this.buffer.Length; 313 | int newLength = curLength * 2; 314 | while (newLength <= curLength + sizeof (uint)) 315 | newLength *= 2; 316 | 317 | byte[] newbuffer = new byte[newLength]; 318 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 319 | this.buffer = newbuffer; 320 | } 321 | 322 | #if SAFE 323 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (uint)); 324 | #else 325 | fixed (byte* ub = this.buffer) 326 | *((uint*) (ub + this.position)) = value; 327 | #endif 328 | 329 | this.position += sizeof (uint); 330 | } 331 | 332 | public void WriteUInt64 (ulong value) 333 | { 334 | if (this.position + sizeof(ulong) >= this.buffer.Length) 335 | { 336 | int curLength = this.buffer.Length; 337 | int newLength = curLength * 2; 338 | while (newLength <= curLength + sizeof(ulong)) 339 | newLength *= 2; 340 | 341 | byte[] newbuffer = new byte[newLength]; 342 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 343 | this.buffer = newbuffer; 344 | } 345 | 346 | #if SAFE 347 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (ulong)); 348 | #else 349 | fixed (byte* ub = this.buffer) 350 | *((ulong*) (ub + this.position)) = value; 351 | #endif 352 | 353 | this.position += sizeof (ulong); 354 | } 355 | 356 | public void WriteDecimal (decimal value) 357 | { 358 | int[] parts = Decimal.GetBits (value); 359 | for (int i = 0; i < parts.Length; ++i) 360 | WriteInt32 (parts[i]); 361 | } 362 | 363 | public void WriteSingle (float value) 364 | { 365 | if (this.position + sizeof(float) >= this.buffer.Length) 366 | { 367 | int curLength = this.buffer.Length; 368 | int newLength = curLength * 2; 369 | while (newLength <= curLength + sizeof(float)) 370 | newLength *= 2; 371 | 372 | byte[] newbuffer = new byte[newLength]; 373 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 374 | this.buffer = newbuffer; 375 | } 376 | 377 | #if SAFE 378 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (float)); 379 | #else 380 | fixed (byte* ub = this.buffer) 381 | *((float*) (ub + this.position)) = value; 382 | #endif 383 | 384 | this.position += sizeof (float); 385 | } 386 | 387 | public void WriteDouble (double value) 388 | { 389 | if (this.position + sizeof (double) >= this.buffer.Length) 390 | { 391 | int curLength = this.buffer.Length; 392 | int newLength = curLength * 2; 393 | while (newLength <= curLength + sizeof (double)) 394 | newLength *= 2; 395 | 396 | byte[] newbuffer = new byte[newLength]; 397 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 398 | this.buffer = newbuffer; 399 | } 400 | 401 | #if SAFE 402 | Buff.BlockCopy (BitConverter.GetBytes (value), 0, this.buffer, this.position, sizeof (double)); 403 | #else 404 | fixed (byte* ub = this.buffer) 405 | *((double*) (ub + this.position)) = value; 406 | #endif 407 | 408 | this.position += sizeof (double); 409 | } 410 | 411 | public void WriteString (Encoding encoding, string value) 412 | { 413 | if (encoding == null) 414 | throw new ArgumentNullException ("encoding"); 415 | 416 | if (value == null) 417 | { 418 | Write7BitEncodedInt (-1); 419 | return; 420 | } 421 | 422 | byte[] data = encoding.GetBytes (value); 423 | int additionalCapacity = sizeof(int) + data.Length; 424 | if (this.position + additionalCapacity >= this.buffer.Length) 425 | { 426 | int curLength = this.buffer.Length; 427 | int newLength = curLength * 2; 428 | while (newLength <= curLength + additionalCapacity) 429 | newLength *= 2; 430 | 431 | byte[] newbuffer = new byte[newLength]; 432 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 433 | this.buffer = newbuffer; 434 | } 435 | 436 | Write7BitEncodedInt (data.Length); 437 | Buff.BlockCopy (data, 0, this.buffer, this.position, data.Length); 438 | this.position += data.Length; 439 | } 440 | 441 | public void Flush() 442 | { 443 | this.position = 0; 444 | } 445 | 446 | public void Pad (int count) 447 | { 448 | if (this.position + count >= this.buffer.Length) 449 | { 450 | int curLength = this.buffer.Length; 451 | int newLength = curLength * 2; 452 | while (newLength <= curLength + count) 453 | newLength *= 2; 454 | 455 | byte[] newbuffer = new byte[newLength]; 456 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, this.Length); 457 | this.buffer = newbuffer; 458 | } 459 | 460 | this.position += count; 461 | } 462 | 463 | public byte[] ToArray() 464 | { 465 | byte[] value = new byte[Length]; 466 | Buff.BlockCopy (this.buffer, 0, value, 0, Length); 467 | 468 | return value; 469 | } 470 | 471 | private byte[] buffer; 472 | private int position; 473 | 474 | private void Write7BitEncodedInt (int value) 475 | { 476 | uint v = (uint) value; 477 | while (v >= 128) 478 | { 479 | WriteByte ((byte) (v | 128)); 480 | v >>= 7; 481 | } 482 | 483 | WriteByte ((byte) v); 484 | } 485 | 486 | private void EnsureAdditionalCapacity (int additionalCapacity) 487 | { 488 | if (this.position + additionalCapacity >= this.buffer.Length) 489 | { 490 | int curLength = this.buffer.Length; 491 | int newLength = curLength * 2; 492 | while (newLength <= curLength + additionalCapacity) 493 | newLength *= 2; 494 | 495 | byte[] newbuffer = new byte[newLength]; 496 | Buff.BlockCopy (this.buffer, 0, newbuffer, 0, Length); 497 | this.buffer = newbuffer; 498 | } 499 | } 500 | } 501 | } --------------------------------------------------------------------------------