Beispiel #1
0
 /// <summary>Stops allowing traversal of routers/firewalls to this host.</summary>
 /// <remarks>This will stop this NAT traversal session.</remarks>
 public void Disable()
 {
     if (!disabled)
     {
         // stop notification thread by sending a message from this local endpoint to itself
         byte[] emptyDatagram = new byte[0];
         PortSpoofing.Send(LocalEndPoint, LocalEndPoint, emptyDatagram);
         disabled = true;
     }
 }
Beispiel #2
0
 /// <summary>Notifies NAT traversal service that a player has joined (for statistical purpose).</summary>
 /// <param name="serverPrivatePort">The private port of the host.</param>
 /// <param name="clientIp">The IP address of the client.</param>
 /// <param name="clientPort">The port of the client.</param>
 public unsafe static void NotifyPlayerHasJoined(INatTraversalSession session, IPAddress clientIp, int clientPort)
 {
     try {
         // send "player_has_joined" message, from the same port as DirectPlay (spoofed)
         // connect to service to determine end points
         using (Socket dummySocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) {
             dummySocket.Connect(natTraversalServiceUrl, natTraversalServicePort);
             IPEndPoint         serviceEndPoint    = (IPEndPoint)dummySocket.RemoteEndPoint;
             IPEndPoint         localEndPoint      = (IPEndPoint)dummySocket.LocalEndPoint;
             IPEndPoint         spoofedEndPoint    = new IPEndPoint(localEndPoint.Address, ((NatTraversalSession)session).PrivatePort);
             MsgPlayerHasJoined msgPlayerHasJoined = new MsgPlayerHasJoined(session.SessionId, clientIp, (ushort)clientPort);
             byte[]             datagram           = new byte[sizeof(MsgPlayerHasJoined)];
             for (int i = 0; i < datagram.Length; ++i)
             {
                 datagram[i] = ((byte *)&msgPlayerHasJoined)[i];
             }
             PortSpoofing.Send(spoofedEndPoint, serviceEndPoint, datagram);
         }
     } catch { }
 }
Beispiel #3
0
        private static void keepAlive(object state)
        {
            KeepAliveSettings settings = (KeepAliveSettings)state;

            PortSpoofing.Send(settings.SpoofedLocalEndPoint, settings.ServiceEndPoint, settings.Datagram);
        }
Beispiel #4
0
        private static unsafe void notificationLoop(object natTraversalSession)
        {
            NatTraversalSession session = (NatTraversalSession)natTraversalSession;
            Timer keepAliveTimer        = null;

            try {
                IPEndPoint serviceEndPoint = null;

                // connect to service to open a port on the NAT devices
                using (Socket notificationSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) {
                    notificationSocket.Connect(natTraversalServiceUrl, natTraversalServicePort);
                    serviceEndPoint       = (IPEndPoint)notificationSocket.RemoteEndPoint;
                    session.LocalEndPoint = (IPEndPoint)notificationSocket.LocalEndPoint;
                }

                // create a new socket in listening mode using the port just opened
                using (Socket notificationSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) {
                    notificationSocket.ReceiveTimeout = 1000;
                    notificationSocket.Bind(session.LocalEndPoint);

                    // send "open notification channel" message
                    {
                        MsgOpenNotificationChannel msg = new MsgOpenNotificationChannel(session.SessionId);
                        byte[] datagram = new byte[sizeof(MsgOpenNotificationChannel)];
                        for (int i = 0; i < datagram.Length; ++i)
                        {
                            datagram[i] = ((byte *)&msg)[i];
                        }
                        notificationSocket.SendTo(datagram, serviceEndPoint);
                    }

                    while (true)
                    {
                        // block until a message is received or 1 second has elapsed
                        EndPoint   any                 = new IPEndPoint(IPAddress.Any, 0);
                        byte[]     bytesReceived       = new byte[1024];
                        int        bytesReceivedLength = notificationSocket.ReceiveFrom(bytesReceived, ref any);
                        IPEndPoint remoteEndPoint      = (IPEndPoint)any;

                        // test for stop condition: a message send from this local endpoint to itself
                        if (remoteEndPoint.Equals(session.LocalEndPoint))
                        {
                            break;
                        }
                        else if (remoteEndPoint.Equals(serviceEndPoint))
                        {
                            // handle the message
                            if (bytesReceivedLength >= 3 && bytesReceived[0] == 'z' && bytesReceived[1] == 't')
                            {
                                switch (bytesReceived[2])
                                {
                                case (byte)MessageType.AckOpenNotificationChannel:
                                    if (bytesReceivedLength == sizeof(MsgAckOpenNotificationChannel))
                                    {
                                        MsgAckOpenNotificationChannel msg = new MsgAckOpenNotificationChannel();
                                        for (int i = 0; i < sizeof(MsgAckOpenNotificationChannel); ++i)
                                        {
                                            ((byte *)&msg)[i] = bytesReceived[i];
                                        }

                                        // handle MsgAckOpenNotificationChannel
                                        // send "host" message, from the same port as DirectPlay (spoofed)
                                        IPEndPoint spoofedEndPoint = new IPEndPoint(session.LocalEndPoint.Address, session.PrivatePort);
                                        MsgHost    msgHost         = new MsgHost(session.SessionId, session.LocalEndPoint.Address, (ushort)session.PrivatePort);
                                        byte[]     datagram        = new byte[sizeof(MsgHost)];
                                        for (int i = 0; i < datagram.Length; ++i)
                                        {
                                            datagram[i] = ((byte *)&msgHost)[i];
                                        }
                                        PortSpoofing.Send(spoofedEndPoint, serviceEndPoint, datagram);
                                    }
                                    break;

                                case (byte)MessageType.AckHost:
                                    if (bytesReceivedLength == sizeof(MsgAckHost))
                                    {
                                        if (keepAliveTimer == null)                                                     // ignore if already handled
                                        {
                                            MsgAckHost msg = new MsgAckHost();
                                            for (int i = 0; i < sizeof(MsgAckHost); ++i)
                                            {
                                                ((byte *)&msg)[i] = bytesReceived[i];
                                            }

                                            // handle MsgAckHost
                                            // record public address/port
                                            session.PublicIpAddress = new IPAddress((uint)IPAddress.HostToNetworkOrder((int)msg.HostPublicIp)).ToString();
                                            session.PublicPort      = msg.HostPublicPort;

                                            // notify main thread that NAT traversal is enabled
                                            session.Enabled = true;
                                            if (session.NatTraversalEnabled != null)
                                            {
                                                session.NatTraversalEnabled.Set();
                                            }

                                            // set reception so it can't time-out
                                            notificationSocket.ReceiveTimeout = 0;

                                            // send a keep alive message every 19 seconds
                                            KeepAliveSettings settings = new KeepAliveSettings();
                                            settings.SpoofedLocalEndPoint = session.LocalEndPoint;
                                            settings.ServiceEndPoint      = serviceEndPoint;
                                            MsgKeepAlive msgKeepAlive = new MsgKeepAlive(session.SessionId);
                                            settings.Datagram = new byte[sizeof(MsgKeepAlive)];
                                            for (int i = 0; i < settings.Datagram.Length; ++i)
                                            {
                                                settings.Datagram[i] = ((byte *)&msgKeepAlive)[i];
                                            }

                                            keepAliveTimer = new Timer(new TimerCallback(keepAlive), settings, 19000, 19000);
                                        }
                                    }
                                    break;

                                case (byte)MessageType.Connect:
                                    if (bytesReceivedLength == sizeof(MsgConnect))
                                    {
                                        MsgConnect msg = new MsgConnect();
                                        for (int i = 0; i < sizeof(MsgConnect); ++i)
                                        {
                                            ((byte *)&msg)[i] = bytesReceived[i];
                                        }

                                        // handle MsgConnect
                                        // punch hole targeting client's public and private addresses
                                        IPAddress publicClientAddress  = new IPAddress((uint)IPAddress.HostToNetworkOrder((int)msg.ClientPublicIp));
                                        IPAddress privateClientAddress = new IPAddress((uint)IPAddress.HostToNetworkOrder((int)msg.ClientPrivateIp));

                                        IPEndPoint spoofedEndPoint = new IPEndPoint(session.LocalEndPoint.Address, session.PrivatePort);
                                        byte[]     emptyDatagram   = new byte[0];

                                        PortSpoofing.Send(spoofedEndPoint, new IPEndPoint(publicClientAddress, msg.ClientPublicPort), emptyDatagram);
                                        PortSpoofing.Send(spoofedEndPoint, new IPEndPoint(privateClientAddress, msg.ClientPrivatePort), emptyDatagram);

                                        // send acknowledgement through notification channel
                                        MsgAckConnect msgAckConnect = new MsgAckConnect(session.SessionId, publicClientAddress, msg.ClientPublicPort);
                                        byte[]        datagram      = new byte[sizeof(MsgAckConnect)];
                                        for (int i = 0; i < datagram.Length; ++i)
                                        {
                                            datagram[i] = ((byte *)&msgAckConnect)[i];
                                        }
                                        notificationSocket.SendTo(datagram, serviceEndPoint);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            } catch {
                if (!session.Enabled && session.NatTraversalEnabled != null)
                {
                    session.NatTraversalEnabled.Set();
                }
            } finally {
                if (keepAliveTimer != null)
                {
                    keepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    keepAliveTimer.Dispose();
                }
            }
        }