public override NetSendResult Enqueue(NetOutgoingMessage message)
        {
            int queueLen = QueuedSends.Count + 1;
            int left     = GetAllowedSends();

            if (queueLen > left ||
                (message.ByteLength > _connection.CurrentMTU &&
                 _connection._peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.DropAboveMTU))
            {
                _connection.Peer.Recycle(message);
                return(NetSendResult.Dropped);
            }

            if (message.BitLength >= ushort.MaxValue &&
                _connection._peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.IgnoreMTU)
            {
                _connection.Peer.LogError(NetLogMessage.FromValues(NetLogCode.MessageSizeExceeded,
                                                                   endPoint: _connection,
                                                                   value: message.BitLength,
                                                                   maxValue: ushort.MaxValue));

                return(NetSendResult.Dropped);
            }

            QueuedSends.Enqueue(message);
            return(NetSendResult.Sent);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Create a connection to a remote endpoint.
        /// </summary>
        public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage?hailMessage)
        {
            if (remoteEndPoint == null)
            {
                throw new ArgumentNullException(nameof(remoteEndPoint));
            }

            if (Configuration.DualStack)
            {
                remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint);
            }

            if (Status == NetPeerStatus.NotRunning)
            {
                throw new LidgrenException("Must call Start() first.");
            }

            if (ConnectionLookup.ContainsKey(remoteEndPoint))
            {
                throw new LidgrenException("Already connected to that endpoint!");
            }

            if (Handshakes.TryGetValue(remoteEndPoint, out NetConnection? hs))
            {
                // already trying to connect to that endpoint; make another try
                switch (hs.Status)
                {
                case NetConnectionStatus.InitiatedConnect:
                    // send another connect
                    hs._connectRequested = true;
                    break;

                case NetConnectionStatus.RespondedConnect:
                    // send another response
                    hs.SendConnectResponse(NetTime.Now, false);
                    break;

                default:
                    // weird
                    LogWarning(NetLogMessage.FromValues(NetLogCode.UnexpectedHandshakeStatus, value: (int)hs.Status));
                    break;
                }
                return(hs);
            }

            var conn = new NetConnection(this, remoteEndPoint);

            conn.Status           = NetConnectionStatus.InitiatedConnect;
            conn.LocalHailMessage = hailMessage;

            // handle on network thread
            conn._connectRequested    = true;
            conn._connectionInitiator = true;

            Handshakes.TryAdd(remoteEndPoint, conn);
            return(conn);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Approves this connection; sending a connection response to the remote host.
        /// </summary>
        /// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
        public void Approve(NetOutgoingMessage?localHail)
        {
            if (Status != NetConnectionStatus.RespondedAwaitingApproval)
            {
                Peer.LogWarning(NetLogMessage.FromValues(NetLogCode.UnexpectedApprove, endPoint: this, value: (int)Status));
                return;
            }

            LocalHailMessage   = localHail;
            _handshakeAttempts = 0;
            SendConnectResponse(NetTime.Now, false);
        }
        internal bool SendMTUPacket(int byteCount, IPEndPoint target)
        {
            if (Socket == null)
            {
                throw new InvalidOperationException("No socket bound.");
            }

            try
            {
                Socket.DontFragment = true;

                int bytesSent = Socket.SendTo(_sendBuffer, 0, byteCount, SocketFlags.None, target);
                if (byteCount != bytesSent)
                {
                    LogWarning(NetLogMessage.FromValues(NetLogCode.FullSendFailure,
                                                        endPoint: target,
                                                        value: bytesSent,
                                                        maxValue: byteCount));
                }
                Statistics.PacketSent(byteCount, 1);
            }
            catch (SocketException sx)
            {
                switch (sx.SocketErrorCode)
                {
                case SocketError.MessageSize:
                    return(false);

                case SocketError.WouldBlock:
                    // send buffer full?
                    LogError(new NetLogMessage(NetLogCode.SocketWouldBlock, sx, target));
                    return(true);

                case SocketError.ConnectionReset:
                    return(true);

                default:
                    LogError(new NetLogMessage(NetLogCode.SendFailure, sx, target));
                    break;
                }
            }
            catch (Exception ex)
            {
                LogError(new NetLogMessage(NetLogCode.SendFailure, ex, target));
            }
            finally
            {
                Socket.DontFragment = false;
            }
            return(true);
        }
Ejemplo n.º 5
0
        private void FinalizeMTU(int size)
        {
            if (_expandMTUStatus == ExpandMTUStatus.Finished)
            {
                return;
            }

            _expandMTUStatus = ExpandMTUStatus.Finished;

            int previousMTU = CurrentMTU;

            CurrentMTU = size;
            if (CurrentMTU != _peerConfiguration._maximumTransmissionUnit)
            {
                Peer.LogDebug(NetLogMessage.FromValues(NetLogCode.ExpandedMTU,
                                                       endPoint: this,
                                                       value: previousMTU,
                                                       maxValue: CurrentMTU));
            }
        }
Ejemplo n.º 6
0
        private void WriteLocalHail(NetOutgoingMessage om)
        {
            if (LocalHailMessage == null)
            {
                return;
            }

            int bitsToAppend = LocalHailMessage.BitLength - LocalHailMessage.BitPosition;

            if (bitsToAppend > 0)
            {
                int resultSize = om.ByteLength + NetBitWriter.BytesForBits(bitsToAppend);
                int maxMtuSize = _peerConfiguration._maximumTransmissionUnit - 10;
                if (resultSize > maxMtuSize)
                {
                    int maxHailSize = maxMtuSize - om.ByteLength;
                    Peer.LogError(NetLogMessage.FromValues(NetLogCode.HailMessageTooLarge, value: resultSize, maxValue: maxHailSize));
                }

                om.Write(LocalHailMessage);
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Called when host/client receives a NatIntroduction message from a master server
        /// </summary>
        internal void HandleNatIntroduction(int offset)
        {
            AssertIsOnLibraryThread();

            // read intro
            NetIncomingMessage tmp = SetupReadHelperMessage(offset, 1000); // never mind length

            byte       hostByte       = tmp.ReadByte();
            IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
            IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
            string     token          = tmp.ReadString();
            bool       isHost         = hostByte != 0;

            LogDebug(NetLogMessage.FromValues(NetLogCode.NATIntroductionReceived, value: hostByte));

            if (!isHost && !Configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess))
            {
                return; // no need to punch - we're not listening for nat intros!
            }
            // send internal punch
            var internalPunch = CreateMessage(1);

            internalPunch._messageType = NetMessageType.NatPunchMessage;
            internalPunch.Write(hostByte);
            internalPunch.Write(token);
            UnsentUnconnectedMessages.Enqueue((remoteInternal, internalPunch));
            LogDebug(new NetLogMessage(NetLogCode.NATPunchSent, endPoint: remoteInternal));

            // send external punch
            var externalPunch = CreateMessage(1);

            externalPunch._messageType = NetMessageType.NatPunchMessage;
            externalPunch.Write(hostByte);
            externalPunch.Write(token);
            UnsentUnconnectedMessages.Enqueue((remoteExternal, externalPunch));
            LogDebug(new NetLogMessage(NetLogCode.NATPunchSent, endPoint: remoteExternal));
        }
        // TODO: replace byte[] with Memory<byte> in the future (held back by Socket.SendTo)
        // https://github.com/dotnet/runtime/issues/33418
        internal NetSocketResult ActuallySendPacket(byte[] data, int byteCount, IPEndPoint target)
        {
            Socket?socket = Socket;

            Debug.Assert(socket != null);

            bool broadcasting = false;

            try
            {
                IPAddress?ba = NetUtility.GetBroadcastAddress();

                // TODO: refactor this check outta here
                if (target.Address.Equals(ba))
                {
                    // Some networks do not allow
                    // a global broadcast so we use the BroadcastAddress from the configuration
                    // this can be resolved to a local broadcast addresss e.g 192.168.x.255
                    _targetCopy.Address = Configuration.BroadcastAddress;
                    _targetCopy.Port    = target.Port;

                    socket.EnableBroadcast = true;
                    broadcasting           = true;
                }
                else if (
                    Configuration.DualStack &&
                    Configuration.LocalAddress.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    // Maps to IPv6 for Dual Mode
                    NetUtility.MapToIPv6(target, _targetCopy);
                }
                else
                {
                    _targetCopy.Port    = target.Port;
                    _targetCopy.Address = target.Address;
                }

                int bytesSent = socket.SendTo(data, 0, byteCount, SocketFlags.None, _targetCopy);
                if (byteCount != bytesSent)
                {
                    LogWarning(NetLogMessage.FromValues(NetLogCode.FullSendFailure,
                                                        endPoint: target,
                                                        value: bytesSent,
                                                        maxValue: byteCount));
                }
                //LogDebug("Sent " + numBytes + " bytes");
            }
            catch (SocketException sx)
            {
                switch (sx.SocketErrorCode)
                {
                case SocketError.WouldBlock:
                    // send buffer full?
                    LogDebug(new NetLogMessage(NetLogCode.SocketWouldBlock, sx));
                    return(new NetSocketResult(false, false));

                case SocketError.ConnectionReset:
                    // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                    return(new NetSocketResult(false, true));

                default:
                    LogError(new NetLogMessage(NetLogCode.SendFailure, sx, target));
                    break;
                }
            }
            catch (Exception ex)
            {
                LogError(new NetLogMessage(NetLogCode.SendFailure, ex, target));
            }
            finally
            {
                if (broadcasting)
                {
                    socket.EnableBroadcast = false;
                }
            }
            return(new NetSocketResult(true, false));
        }
Ejemplo n.º 9
0
        internal void ReceivedHandshake(TimeSpan now, NetMessageType type, int offset, int payloadLength)
        {
            Peer.AssertIsOnLibraryThread();

            switch (type)
            {
            case NetMessageType.Connect:
                if (Status == NetConnectionStatus.ReceivedInitiation)
                {
                    // Whee! Server full has already been checked
                    var(success, hail, hailLength) = ValidateHandshakeData(offset, payloadLength);
                    if (success)
                    {
                        if (hail != null)
                        {
                            RemoteHailMessage = Peer.CreateIncomingMessage(NetIncomingMessageType.Data);
                            RemoteHailMessage.SetBuffer(hail, true);
                            RemoteHailMessage.ByteLength = hailLength;
                        }
                        else
                        {
                            RemoteHailMessage = null;
                        }

                        if (_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
                        {
                            // ok, let's not add connection just yet
                            var appMsg = Peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval);

                            appMsg.ReceiveTime      = now;
                            appMsg.SenderConnection = this;
                            appMsg.SenderEndPoint   = RemoteEndPoint;

                            if (RemoteHailMessage != null)
                            {
                                appMsg.Write(RemoteHailMessage.GetBuffer().AsSpan(0, RemoteHailMessage.ByteLength));
                            }

                            SetStatus(NetConnectionStatus.RespondedAwaitingApproval);
                            Peer.ReleaseMessage(appMsg);
                            return;
                        }

                        SendConnectResponse(now, true);
                    }
                    return;
                }
                if (Status == NetConnectionStatus.RespondedAwaitingApproval)
                {
                    Peer.LogWarning(new NetLogMessage(NetLogCode.IgnoringMultipleConnects, endPoint: this));
                    return;
                }
                if (Status == NetConnectionStatus.RespondedConnect)
                {
                    // our ConnectResponse must have been lost
                    SendConnectResponse(now, true);
                    return;
                }
                Peer.LogDebug(NetLogMessage.FromValues(NetLogCode.UnhandledConnect, endPoint: this, value: (int)Status));
                break;

            case NetMessageType.ConnectResponse:
                HandleConnectResponse(offset, payloadLength);
                break;

            case NetMessageType.ConnectionEstablished:
                switch (Status)
                {
                case NetConnectionStatus.Connected:
                    // ok...
                    break;

                case NetConnectionStatus.Disconnected:
                case NetConnectionStatus.Disconnecting:
                case NetConnectionStatus.None:
                    // too bad, almost made it
                    break;

                case NetConnectionStatus.ReceivedInitiation:
                    // uh, a little premature... ignore
                    break;

                case NetConnectionStatus.InitiatedConnect:
                    // weird, should have been RespondedConnect...
                    break;

                case NetConnectionStatus.RespondedConnect:
                    // awesome
                    NetIncomingMessage msg = Peer.SetupReadHelperMessage(offset, payloadLength);
                    InitializeRemoteTimeOffset(msg.ReadTimeSpan());

                    Peer.AcceptConnection(this);
                    InitializePing();
                    SetStatus(NetConnectionStatus.Connected);
                    return;
                }
                break;

            case NetMessageType.InvalidHandshake:
            case NetMessageType.WrongAppIdentifier:
            case NetMessageType.ConnectTimedOut:
            case NetMessageType.TimedOut:
            case NetMessageType.Disconnect:
                NetOutgoingMessage?reason = null;
                try
                {
                    reason = Peer.CreateReadHelperOutMessage(offset, payloadLength);
                }
                catch
                {
                }
                ExecuteDisconnect(reason, false);
                break;

            case NetMessageType.Discovery:
                Peer.HandleIncomingDiscoveryRequest(now, RemoteEndPoint, offset, payloadLength);
                return;

            case NetMessageType.DiscoveryResponse:
                Peer.HandleIncomingDiscoveryResponse(now, RemoteEndPoint, offset, payloadLength);
                return;

            case NetMessageType.Ping:
                // silently ignore
                return;

            default:
                Peer.LogDebug(NetLogMessage.FromValues(NetLogCode.UnhandledHandshakeMessage, value: (int)Status));
                break;
            }
        }
Ejemplo n.º 10
0
        // heartbeat called when connection still is in m_handshakes of NetPeer
        internal void UnconnectedHeartbeat(TimeSpan now)
        {
            Peer.AssertIsOnLibraryThread();

            if (_disconnectRequested)
            {
                ExecuteDisconnect(_disconnectMessage, true);
            }

            if (_connectRequested)
            {
                switch (Status)
                {
                case NetConnectionStatus.Connected:
                case NetConnectionStatus.RespondedConnect:
                    // reconnect
                    ExecuteDisconnect(NetMessageType.ConnectTimedOut);
                    break;

                case NetConnectionStatus.InitiatedConnect:
                    // send another connect attempt
                    SendConnect(now);
                    break;

                case NetConnectionStatus.Disconnected:
                    Peer.LogError(NetLogMessage.FromValues(NetLogCode.DisconnectedPeer));
                    break;

                case NetConnectionStatus.Disconnecting:
                    // let disconnect finish first
                    break;

                case NetConnectionStatus.None:
                default:
                    SendConnect(now);
                    break;
                }
                return;
            }

            if (now - _lastHandshakeSendTime > _peerConfiguration._resendHandshakeInterval)
            {
                if (_handshakeAttempts >= _peerConfiguration._maximumHandshakeAttempts)
                {
                    // failed to connect
                    ExecuteDisconnect(NetMessageType.ConnectTimedOut);
                    return;
                }

                // resend handshake
                switch (Status)
                {
                case NetConnectionStatus.InitiatedConnect:
                    SendConnect(now);
                    break;

                case NetConnectionStatus.RespondedConnect:
                    SendConnectResponse(now, true);
                    break;

                case NetConnectionStatus.RespondedAwaitingApproval:
                    // awaiting approval
                    _lastHandshakeSendTime = now;     // postpone handshake resend
                    break;

                case NetConnectionStatus.None:
                case NetConnectionStatus.ReceivedInitiation:
                default:
                    Peer.LogWarning(NetLogMessage.FromValues(NetLogCode.UnexpectedHandshakeStatus, value: (int)Status));
                    break;
                }
            }
        }