/// <summary> /// Decodes a message object. /// The format looks as follows: 28 bit P2P version, 4 bit message type, 32 bit message ID, 8 bit message command, /// 160 bit senderSocket ID, 16 bit senderSocket TCP port, 16 bit senderSocket UDP port, 160 bit recipientSocket ID, 32 bit content types, 8 bit options. /// In total, the header is of size 58 bytes. /// </summary> /// <param name="buffer">The buffer to decode from.</param> /// <param name="recipientSocket">The recipientSocket of the message.</param> /// <param name="senderSocket">The senderSocket of the packet, which has been set in the socket class.</param> // TODO check if true /// <returns>The partial message where only the header fields are set.</returns> public static Message DecodeHeader(AlternativeCompositeByteBuf buffer, IPEndPoint recipientSocket, IPEndPoint senderSocket) { Logger.Debug("Decode message. Recipient: {0}, Sender: {1}", recipientSocket, senderSocket); var message = new Message(); int versionAndType = buffer.ReadInt(); // 4 message.SetVersion(versionAndType >> 4); message.SetType((Message.MessageType)(versionAndType & Utils.Utils.Mask0F)); // TODO does this work? (2x) message.SetMessageId(buffer.ReadInt()); // 8 message.SetCommand(buffer.ReadByte()); // 9 // TODO check conversion with Java version var senderId = ReadId(buffer); // 29 int tcpPort = buffer.ReadUShort(); // 31 // TODO check if should be read as short (same as encode) int udpPort = buffer.ReadUShort(); // 33 var recipientId = ReadId(buffer); // 53 int contentTypes = buffer.ReadInt(); // 57 int options = buffer.ReadUByte(); // 58 // TODO check if should be read as unsigned/signed message.SetRecipient(new PeerAddress(recipientId, recipientSocket)); message.HasContent(contentTypes != 0); message.SetContentType(DecodeContentTypes(contentTypes, message)); message.SetOptions(options & Utils.Utils.Mask0F); // set the address as we see it, important for port forwarding identification int senderOptions = options >> 4; var pa = new PeerAddress(senderId, senderSocket.Address, tcpPort, udpPort, senderOptions); message.SetSender(pa); message.SetSenderSocket(senderSocket); message.SetRecipientSocket(recipientSocket); return message; }
/// <summary> /// Encodes a message object. /// The format looks as follows: 28 bit P2P version, 4 bit message type, 32 bit message ID, 8 bit message command, /// 160 bit senderSocket ID, 16 bit senderSocket TCP port, 16 bit senderSocket UDP port, 160 bit recipientSocket ID, 32 bit content types, 8 bit options. /// In total, the header is of size 58 bytes. /// </summary> /// <param name="buffer">The buffer to encode to.</param> /// <param name="message">The message with the header that will be encoded.</param> public static void EncodeHeader(AlternativeCompositeByteBuf buffer, Message message) { // TODO add log statemet, also in Java version int versionAndType = message.Version << 4 | ((int)message.Type & Utils.Utils.Mask0F); // TODO check if ordinal works buffer.WriteInt(versionAndType); // 4 buffer.WriteInt(message.MessageId); // 8 buffer.WriteByte(message.Command); // 9 buffer.WriteBytes(message.Sender.PeerId.ToByteArray()); // 29 buffer.WriteShort((short) message.Sender.TcpPort); // 31 buffer.WriteShort((short) message.Sender.UdpPort); // 33 buffer.WriteBytes(message.Recipient.PeerId.ToByteArray()); // 53 buffer.WriteInt(EncodeContentTypes(message.ContentTypes)); // 57 buffer.WriteByte((sbyte) (message.Sender.Options << 4 | message.Options)); // 58 // TODO check if works }
public static Message CreateDummyMessage(Number160 idSender, String inetSender, int tcpPortSendor, int udpPortSender, Number160 idRecipient, String inetRecipient, int tcpPortRecipient, int udpPortRecipient, sbyte command, Message.MessageType type, bool firewallUdp, bool firewallTcp) { var message = new Message(); PeerAddress n1 = CreateAddress(idSender, inetSender, tcpPortSendor, udpPortSender, firewallUdp, firewallTcp); message.SetSender(n1); // PeerAddress n2 = CreateAddress(idRecipient, inetRecipient, tcpPortRecipient, udpPortRecipient, firewallUdp, firewallTcp); message.SetRecipient(n2); message.SetType(type); message.SetCommand(command); return message; }
public override void HandleResponse(Message.Message requestMessage, PeerConnection peerConnection, bool sign, IResponder responder) { if (!(requestMessage.Type == Message.Message.MessageType.RequestFf1 && requestMessage.Command == Rpc.Commands.Broadcast.GetNr())) { throw new ArgumentException("Message content is wrong for this handler."); } Logger.Debug("Received BROADCAST message: {0}.", requestMessage); BroadcastHandler.Receive(requestMessage); if (requestMessage.IsUdp) { responder.ResponseFireAndForget(); } else { responder.Response(CreateResponseMessage(requestMessage, Message.Message.MessageType.Ok)); } }
/// <summary> /// .NET-specific: Used for DistributedRouting only. /// </summary> internal TaskCompletionSource<Message.Message> CloseNeighborsTcs(PeerAddress remotePeer, SearchValues searchValues, Message.Message.MessageType type, ChannelCreator channelCreator, IConnectionConfiguration configuration) { var message = CreateRequestMessage(remotePeer, Rpc.Commands.Neighbor.GetNr(), type); if (!message.IsRequest()) { throw new ArgumentException("The type must be a request."); } message.SetKey(searchValues.LocationKey); message.SetKey(searchValues.DomainKey ?? Number160.Zero); if (searchValues.From != null && searchValues.To != null) { ICollection<Number640> collection = new List<Number640>(); collection.Add(searchValues.From); collection.Add(searchValues.To); var keyCollection = new KeyCollection(collection); message.SetKeyCollection(keyCollection); } else { if (searchValues.ContentKey != null) { message.SetKey(searchValues.ContentKey); } if (searchValues.KeyBloomFilter != null) { message.SetBloomFilter(searchValues.KeyBloomFilter); } if (searchValues.ContentBloomFilter != null) { message.SetBloomFilter(searchValues.ContentBloomFilter); } } return Send(message, configuration, channelCreator); }
private void RemovePeerIfFailed(TaskCompletionSource<Message.Message> tcs, Message.Message message) { // execute the following delegate only if TCS task failed tcs.Task.ContinueWith(delegate(Task task) { if (message.Recipient.IsRelayed) { // TODO: Java, make the relay go away if failed } else { lock (_peerStatusListeners) { foreach (var listener in _peerStatusListeners) { listener.PeerFailed(message.Recipient, new PeerException(tcs)); } } } }, TaskContinuationOptions.OnlyOnFaulted); }
private async Task AfterConnectAsync(TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, IClientChannel channel, bool isFireAndForget) { // TODO use for UDP connections, too // TODO find clean-mechanism to show the channel-creation fails (UDP uses try/catch) // check if channel could be created (due to shutdown) if (channel == null) { string msg = String.Format("Could not create a {0} socket. (Due to shutdown.)", message.IsUdp ? "UDP" : "TCP"); Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } Logger.Debug("About to connect to {0} with channel {1}, ff = {2}.", message.Recipient, channel, isFireAndForget); // sending var sendTask = channel.SendMessageAsync(message); await AfterSendAsync(sendTask, tcsResponse, isFireAndForget, channel); }
private async Task ConnectAndSendAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler, Message.Message message) { var recipient = message.Recipient.CreateSocketTcp(); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); }
/// <summary> /// // TODO document /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="idleTcpSeconds"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRelayAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { var taskPingDone = PingFirst(message.Recipient.PeerSocketAddresses); await taskPingDone; if (!taskPingDone.IsFaulted) { var recipient = PeerSocketAddress.CreateSocketTcp(taskPingDone.Result); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); // TODO add this before AfterConnect? var taskResponse = tcsResponse.Task; await taskResponse; if (taskResponse.IsFaulted) { if (taskResponse.Result != null && taskResponse.Result.Type != Message.Message.MessageType.User1) { // "clearInactivePeerSocketAddress" var tmp = new List<PeerSocketAddress>(); foreach (var psa in message.Recipient.PeerSocketAddresses) { if (psa != null) { if (!psa.Equals(taskPingDone.Result)) { tmp.Add(psa); } } } message.SetPeerSocketAddresses(tmp); await SendTcpAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection); } } } else { // .NET-specific: tcsResponse.SetException(new TaskFailedException("No relay could be contacted. <-> " + taskPingDone.Exception)); } }
/// <summary> /// This method makes a copy of the original message and prepares it for /// sending it to the relay. /// </summary> /// <param name="message"></param> /// <returns></returns> private static Message.Message CreateRconMessage(Message.Message message) { // get relay address from the unreachable peer var relayAddresses = message.Recipient.PeerSocketAddresses.ToArray(); PeerSocketAddress socketAddress; if (relayAddresses.Length > 0) { // we should be fair and choose one of the relays randomly socketAddress = relayAddresses[Utils.Utils.RandomPositiveInt(relayAddresses.Length)]; } else { throw new ArgumentException("There are no PeerSocketAddresses available for this relayed peer. This should not be possible!"); } // we need to make a copy of the original message var rconMessage = new Message.Message(); rconMessage.SetSender(message.Sender); rconMessage.SetVersion(message.Version); rconMessage.SetIntValue(message.MessageId); // make the message ready to send PeerAddress recipient = message.Recipient .ChangeAddress(socketAddress.InetAddress) .ChangePorts(socketAddress.TcpPort, socketAddress.UdpPort) .ChangeIsRelayed(false); rconMessage.SetRecipient(recipient); rconMessage.SetCommand(Rpc.Rpc.Commands.Rcon.GetNr()); rconMessage.SetType(Message.Message.MessageType.Request1); return rconMessage; }
/// <summary> /// This method initiates the reverse connection setup. /// It creates a new message and sends it via relay to the unreachable peer /// which then connects to this peer again. After the connect message from the /// unreachable peer, this peer will send the original message and its content /// directly. /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRconAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { message.SetKeepAlive(true); Logger.Debug("Initiate reverse connection setup to peer with address {0}.", message.Recipient); var rconMessage = CreateRconMessage(message); // TODO works? // cache the original message until the connection is established _cachedRequests.AddOrUpdate(message.MessageId, tcsResponse, (i, source) => tcsResponse); // wait for response (whether the reverse connection setup was successful) var tcsRconResponse = new TaskCompletionSource<Message.Message>(rconMessage); // .NET-specific: specify and use a RconInboundHandler class var rconInboundHandler = new RconInboundHandler(tcsRconResponse, tcsResponse); // send reverse connection request instead of normal message await SendTcpAsync(rconInboundHandler, tcsRconResponse, rconMessage, channelCreator, connectTimeoutMillis, connectTimeoutMillis, peerConnection); }
/// <summary> /// Requests close neighbors from the remote peer. The remote peer may indicate if the /// data is present on that peer. This is an RPC. /// </summary> /// <param name="remotePeer">The remote peer to send this request to.</param> /// <param name="searchValues">The values to search for in the storage.</param> /// <param name="type">The type of the neighbor request: /// - Request1 for Neighbors means check for Put (no digest) for tracker and storage. /// - Request2 for Neighbors means check for Get (with digest) for storage. /// - Request3 for Neighbors means check for Get (with digest) for tracker. /// - Request4 for Neighbors means check for Put (with digest) for task.</param> /// <param name="channelCreator">The channel creator that creates connections.</param> /// <param name="configuration">The client-side connection configuration.</param> /// <returns>The future response message.</returns> public Task<Message.Message> CloseNeighborsAsync(PeerAddress remotePeer, SearchValues searchValues, Message.Message.MessageType type, ChannelCreator channelCreator, IConnectionConfiguration configuration) { var tcsResponse = CloseNeighborsTcs(remotePeer, searchValues, type, channelCreator, configuration); return tcsResponse.Task; }
public override void HandleResponse(Message.Message requestMessage, PeerConnection peerConnection, bool sign, IResponder responder) { if (requestMessage.KeyList.Count < 2) { throw new ArgumentException("At least location and domain keys are needed."); } if (!(requestMessage.Type == Message.Message.MessageType.Request1 || requestMessage.Type == Message.Message.MessageType.Request2 || requestMessage.Type == Message.Message.MessageType.Request3 || requestMessage.Type == Message.Message.MessageType.Request4) && (requestMessage.Command == Rpc.Commands.Neighbor.GetNr())) { throw new ArgumentException("Message content is wrong for this handler."); } Number160 locationKey = requestMessage.Key(0); Number160 domainKey = requestMessage.Key(1); var neighbors = GetNeighbors(locationKey, NeighborSize); if (neighbors == null) { // return empty neighbor set var response = CreateResponseMessage(requestMessage, Message.Message.MessageType.NotFound); response.SetNeighborSet(new NeighborSet(-1, new Collection<PeerAddress>())); responder.Response(response); return; } // create response message and set neighbors var responseMessage = CreateResponseMessage(requestMessage, Message.Message.MessageType.Ok); Logger.Debug("Found the following neighbors: {0}.", Convenient.ToString(neighbors)); var neighborSet = new NeighborSet(NeighborLimit, neighbors); responseMessage.SetNeighborSet(neighborSet); Number160 contentKey = requestMessage.Key(2); var keyBloomFilter = requestMessage.BloomFilter(0); var contentBloomFilter = requestMessage.BloomFilter(1); var keyCollection = requestMessage.KeyCollection(0); // it is important to set an integer if a value is present bool isDigest = requestMessage.Type != Message.Message.MessageType.Request1; if (isDigest) { if (requestMessage.Type == Message.Message.MessageType.Request2) { DigestInfo digestInfo; if (PeerBean.DigestStorage == null) { // no storage to search digestInfo = new DigestInfo(); } else if (contentKey != null && locationKey != null && domainKey != null) { var locationAndDomainKey = new Number320(locationKey, domainKey); var from = new Number640(locationAndDomainKey, contentKey, Number160.Zero); var to = new Number640(locationAndDomainKey, contentKey, Number160.MaxValue); digestInfo = PeerBean.DigestStorage.Digest(from, to, -1, true); } else if ((keyBloomFilter != null || contentBloomFilter != null) && locationKey != null && domainKey != null) { var locationAndDomainKey = new Number320(locationKey, domainKey); digestInfo = PeerBean.DigestStorage.Digest(locationAndDomainKey, keyBloomFilter, contentBloomFilter, -1, true, true); } else if (keyCollection != null && keyCollection.Keys.Count == 2) { var enumerator = keyCollection.Keys.GetEnumerator(); var from = enumerator.MoveNext() ? enumerator.Current : null; // TODO works correctly? var to = enumerator.MoveNext() ? enumerator.Current : null; digestInfo = PeerBean.DigestStorage.Digest(from, to, -1, true); } else if (locationKey != null && domainKey != null) { var locationAndDomainKey = new Number320(locationKey, domainKey); var from = new Number640(locationAndDomainKey, Number160.Zero, Number160.Zero); var to = new Number640(locationAndDomainKey, Number160.MaxValue, Number160.MaxValue); digestInfo = PeerBean.DigestStorage.Digest(from, to, -1, true); } else { Logger.Warn("Did not search for anything."); digestInfo = new DigestInfo(); } responseMessage.SetIntValue(digestInfo.Size); responseMessage.SetKey(digestInfo.KeyDigest); responseMessage.SetKey(digestInfo.ContentDigest); } else if (requestMessage.Type == Message.Message.MessageType.Request3) { DigestInfo digestInfo; if (PeerBean.DigestTracker == null) { // no tracker to search digestInfo = new DigestInfo(); } else { digestInfo = PeerBean.DigestTracker.Digest(locationKey, domainKey, contentKey); if (digestInfo.Size == 0) { Logger.Debug("No entry found on peer {0}.", requestMessage.Recipient); } } responseMessage.SetIntValue(digestInfo.Size); } else if (requestMessage.Type == Message.Message.MessageType.Request4) { lock (PeerBean.PeerStatusListeners) { foreach (var listener in PeerBean.PeerStatusListeners) { listener.PeerFailed(requestMessage.Sender, new PeerException(PeerException.AbortCauseEnum.Shutdown, "shutdown")); } } } } responder.Response(responseMessage); }
private TaskCompletionSource<Message.Message> Send(Message.Message message, IConnectionConfiguration configuration, ChannelCreator channelCreator) { var tcsResponse = new TaskCompletionSource<Message.Message>(message); tcsResponse.Task.ContinueWith(taskResponse => { if (!taskResponse.IsFaulted) { var response = taskResponse.Result; if (response != null) { var neighborSet = response.NeighborsSet(0); if (neighborSet != null) { foreach (var neighbor in neighborSet.Neighbors) { lock (PeerBean.PeerStatusListeners) { foreach (var listener in PeerBean.PeerStatusListeners) { listener.PeerFound(neighbor, response.Sender, null); } } } } } } }); var requestHandler = new RequestHandler(tcsResponse, PeerBean, ConnectionBean, configuration); if (!configuration.IsForceTcp) { requestHandler.SendUdpAsync(channelCreator); } else { requestHandler.SendTcpAsync(channelCreator); } // .NET-specific: Return TCS instead of Task. It's actually the same TCS that is provided with // the RequestHandler c'tor return tcsResponse; }
/// <summary> /// Encodes the 8 content types to an integer (32 bit). /// </summary> /// <param name="contentTypes">The 8 content types to be encoded.</param> /// <returns>The encoded 32 bit integer.</returns> public static int EncodeContentTypes(Message.Content[] contentTypes) { int result = 0; for (int i = 0; i < Message.ContentTypeLength/2; i++) { if (contentTypes[i*2] != Message.Content.Empty) // TODO check port { result |= ((int) contentTypes[i*2] << (i*8)); // TODO check ordinal } if (contentTypes[i*2 + 1] != Message.Content.Empty) { result |= ((int) contentTypes[i*2 + 1] << 4) << (i*8); } } return result; }
/// <summary> /// Sends a message via UDP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the UDP channel.</param> /// <param name="idleUdpSeconds">The idle time of a message until fail.</param> /// <param name="broadcast">True, if the message is to be sent via layer 2 broadcast.</param> /// <returns>The response message or null, if it is fire-and-forget or a failure occurred.</returns> public async Task SendUdpAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleUdpSeconds, bool broadcast) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // relay options if (message.Sender.IsRelayed) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); IList<PeerSocketAddress> relayAddresses = new List<PeerSocketAddress>(message.Recipient.PeerSocketAddresses); Logger.Debug("Send neighbor request to random relay peer {0}.", relayAddresses); if (relayAddresses.Count > 0) { var relayAddress = relayAddresses[_random.NextInt(relayAddresses.Count)]; message.SetRecipientRelay(message.Recipient .ChangePeerSocketAddress(relayAddress) .ChangeIsRelayed(true)); } else { const string msg = "Peer is relayed, but no relay is given."; Logger.Error(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } } // check for invalid UDP connection to unreachable peers if (message.Recipient.IsRelayed && message.Command != Rpc.Rpc.Commands.Neighbor.GetNr() && message.Command != Rpc.Rpc.Commands.Ping.GetNr()) { string msg = String.Format( "Tried to send a UDP message to unreachable peers. Only TCP messages can be sent to unreachable peers: {0}.", message); Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } // pipeline handler setup TimeoutFactory timeoutFactory = CreateTimeoutHandler(tcsResponse, idleUdpSeconds, isFireAndForget); var handlers = new Dictionary<string, IChannelHandler>(); if (!isFireAndForget) { handlers.Add("timeout0", timeoutFactory.CreateIdleStateHandlerTomP2P()); handlers.Add("timeout1", timeoutFactory.CreateTimeHandler()); } handlers.Add("decoder", new TomP2PSinglePacketUdp(ChannelClientConfiguration.SignatureFactory)); handlers.Add("encoder", new TomP2POutbound(false, ChannelClientConfiguration.SignatureFactory)); if (!isFireAndForget) { handlers.Add("handler", handler); } // create UDP channel MyUdpClient udpClient = null; try { udpClient = channelCreator.CreateUdp(broadcast, handlers); } catch (Exception ex) { string msg = "Channel creation failed. " + ex; Logger.Debug(msg); tcsResponse.SetException(ex); // may have been closed by the other side // or it may have been canceled from this side } // "afterConnect" // check if channel could be created (due to shutdown) if (udpClient == null) { const string msg = "Could not create a UDP socket. (Due to shutdown.)"; Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } Logger.Debug("About to connect to {0} with channel {1}, ff = {2}.", message.Recipient, udpClient, isFireAndForget); // send request message // processes client-side outbound pipeline // (await for possible exception re-throw, does not block) await udpClient.SendMessageAsync(message); // if not fire-and-forget, receive response if (isFireAndForget) { Logger.Debug("Fire and forget message {0} sent. Close channel {1} now.", message, udpClient); tcsResponse.SetResult(null); // set FF result } else { // receive response message // processes client-side inbound pipeline await udpClient.ReceiveMessageAsync(); } udpClient.Close(); }
/// <summary> /// Sends a message via TCP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the TCP channel.</param> /// <param name="idleTcpSeconds">The idle time until message fail.</param> /// <param name="connectTimeoutMillis">The idle time for the connection setup.</param> /// <param name="peerConnection"></param> /// <returns></returns> public async Task SendTcpAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // we need to set the neighbors if we use relays if (message.Sender.IsRelayed && message.Sender.PeerSocketAddresses.Count != 0) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); } if (peerConnection != null && peerConnection.Channel != null && peerConnection.Channel.IsOpen) { var channel = SendTcpPeerConnection(peerConnection, handler, channelCreator, tcsResponse); await AfterConnectAsync(tcsResponse, message, channel, isFireAndForget); } else if (channelCreator != null) { var timeoutHandler = CreateTimeoutHandler(tcsResponse, idleTcpSeconds, handler == null); // check relay if (message.Recipient.IsRelayed) { // check if reverse connection is possible if (!message.Sender.IsRelayed) { await HandleRconAsync(handler, tcsResponse, message, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler); } else { await HandleRelayAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection, timeoutHandler); } } // normal connection else { await ConnectAndSendAsync(handler, tcsResponse, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler, message); } } }
/// <summary> /// Decodes the 8 content types from an integer (32 bit). /// </summary> /// <param name="contentTypes">The 8 content types to be decoded.</param> /// <param name="message">The decoded content types.</param> /// <returns></returns> public static Message.Content[] DecodeContentTypes(int contentTypes, Message message) { var result = new Message.Content[Message.ContentTypeLength]; for (int i = 0; i < Message.ContentTypeLength; i++) { var content = (Message.Content) (contentTypes & Utils.Utils.Mask0F); result[i] = content; if (content == Message.Content.PublicKeySignature) { message.SetHintSign(); } contentTypes >>= 4; } return result; }