internal bool HandleRAK(Contact source, Guid sourceGuid, P2PMessage msg) { bool requireAck = false; if (msg.Header.RequireAck) { requireAck = true; P2PMessage ack = msg.CreateAcknowledgement(); if (ack.Header.RequireAck) { // SYN Send(null, source, sourceGuid, ack, DefaultTimeout, delegate(P2PMessage sync) { SyncId = sync.Header.AckIdentifier; Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("SYNC completed for: {0}", this), GetType().Name); }); } else { // ACK Send(null, source, sourceGuid, ack); } } return(requireAck); }
public virtual void SendMessage(P2PMessage msg, P2PMessageSessionEventArgs se) { // if it is a regular message convert it P2PDCMessage p2pMessage = msg as P2PDCMessage; if (p2pMessage == null) { p2pMessage = new P2PDCMessage(msg); } // prepare the message p2pMessage.PrepareMessage(); // this is very bloated! Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose, "Outgoing message:\r\n" + p2pMessage.ToDebugString(), GetType().Name); if (dcSocket != null) { Processor.SendSocketData(dcSocket, p2pMessage.GetBytes(), se); } else { Processor.Send(p2pMessage.GetBytes(), se); } }
internal bool CheckSLPMessage(P2PBridge bridge, Contact source, Guid sourceGuid, P2PMessage msg, SLPMessage slp) { string src = source.Account.ToLowerInvariant(); string target = nsMessageHandler.Owner.Account; if (slp.FromEmailAccount.ToLowerInvariant() != src) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("Received message from '{0}', differing from source '{1}'", slp.Source, src), GetType().Name); return false; } else if (slp.ToEmailAccount.ToLowerInvariant() != target) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("Received P2P message intended for '{0}', not us '{1}'", slp.Target, target), GetType().Name); if (slp.FromEmailAccount == target) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, "We received a message from ourselves?", GetType().Name); } else { SendSLPStatus(bridge, msg, source, sourceGuid, 404, "Not Found"); } return false; } return true; }
public void Add(Contact remote, Guid remoteGuid, P2PMessage message) { lock (this) { base.Add(new P2PSendItem(remote, remoteGuid, message)); } }
private P2PSession FindSessionByExpectedIdentifier(P2PMessage p2pMessage) { if (p2pMessage.Version == P2PVersion.P2PV2) { foreach (P2PSession session in p2pV2Sessions) { uint expected = session.RemoteIdentifier; if (p2pMessage.Header.Identifier == expected) { return(session); } } } else { foreach (P2PSession session in p2pV1Sessions) { uint expected = session.RemoteIdentifier + 1; if (expected == session.RemoteBaseIdentifier) { expected++; } if (p2pMessage.Header.Identifier == expected) { return(session); } } } return(null); }
public void ProcessP2PMessage(Contact source, Guid sourceGuid, P2PMessage p2pMessage) { // HANDLE RAK: RAKs are session independent and mustn't be quoted on bridges. bool requireAck = HandleRAK(source, sourceGuid, p2pMessage); // HANDLE ACK: ACK/NAK to our RAK message if (HandleACK(p2pMessage)) { return; } // PASS TO P2PHandler if (nsMessageHandler.P2PHandler.ProcessP2PMessage(this, source, sourceGuid, p2pMessage)) { return; } if (!requireAck) { // UNHANDLED P2P MESSAGE Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("*******Unhandled P2P message ****** \r\n{0}", p2pMessage.ToDebugString()), GetType().Name); // Keep RemoteID Synchronized, I think we must track remoteIdentifier here... // Send NAK?????? } }
internal bool SendDirectInvite() { // Skip if we're currently using a TCPBridge. if (Remote.DirectBridge != null && Remote.DirectBridge.IsOpen) { return(false); } int netId; string connectionType = ConnectionType(nsMessageHandler, out netId); P2PMessage p2pMessage = new P2PMessage(Version); Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("Connection type set to {0} for session {1}", connectionType, SessionId.ToString())); // Create the message SLPRequestMessage slpMessage = new SLPRequestMessage(RemoteContactEPIDString, MSNSLPRequestMethod.INVITE); slpMessage.Source = LocalContactEPIDString; slpMessage.CSeq = 0; slpMessage.CallId = Invitation.CallId; slpMessage.MaxForwards = 0; slpMessage.ContentType = "application/x-msnmsgr-transreqbody"; slpMessage.BodyValues["Bridges"] = "TCPv1 SBBridge"; slpMessage.BodyValues["Capabilities-Flags"] = "1"; slpMessage.BodyValues["NetID"] = netId.ToString(CultureInfo.InvariantCulture); slpMessage.BodyValues["Conn-Type"] = connectionType; slpMessage.BodyValues["TCP-Conn-Type"] = connectionType; slpMessage.BodyValues["UPnPNat"] = "false"; // UPNP Enabled slpMessage.BodyValues["ICF"] = (connectionType == "Firewall").ToString(); // Firewall enabled slpMessage.BodyValues["Nat-Trav-Msg-Type"] = "WLX-Nat-Trav-Msg-Direct-Connect-Req"; // We support Hashed-Nonce ( 2 way handshake ) Remote.GenerateNewDCKeys(); slpMessage.BodyValues["Hashed-Nonce"] = Remote.dcLocalHashedNonce.ToString("B").ToUpper(CultureInfo.InvariantCulture); p2pMessage.InnerMessage = slpMessage; if (p2pMessage.Version == P2PVersion.P2PV2) { p2pMessage.V2Header.TFCombination = TFCombination.First; } else if (p2pMessage.Version == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } // These 3 step is very important... // 1- Stop sending for this session on sdgbridge until we receive a response to the direct invite or the timeout expires nsMessageHandler.SDGBridge.StopSending(this); // 2- Setup a dc timer. SetupDCTimer(); // 3- Don't pass p2psession to the sdg bridge. Because, sdgbridge stopped this session. nsMessageHandler.SDGBridge.Send(null /*must be null to bypass queueing*/, Remote, RemoteContactEndPointID, p2pMessage); return(true); }
private P2PSession FindSession(P2PMessage msg, SLPMessage slp) { P2PSession p2pSession = null; uint sessionID = (msg != null) ? msg.Header.SessionId : 0; if ((sessionID == 0) && (slp != null)) { if (slp.BodyValues.ContainsKey("SessionID")) { if (!uint.TryParse(slp.BodyValues["SessionID"].Value, out sessionID)) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, "Unable to parse SLP message SessionID", GetType().Name); sessionID = 0; } } if (sessionID == 0) { // We don't get a session ID in BYE requests // So we need to find the session by its call ID P2PVersion p2pVersion = slp.P2PVersion; p2pSession = FindSessionByCallId(slp.CallId, p2pVersion); if (p2pSession != null) { return(p2pSession); } } } // Sometimes we only have a messageID to find the session with... if ((sessionID == 0) && (msg.Header.Identifier != 0)) { p2pSession = FindSessionByExpectedIdentifier(msg); if (p2pSession != null) { return(p2pSession); } } if (sessionID != 0) { p2pSession = FindSessionBySessionId(sessionID, msg.Version); if (p2pSession != null) { return(p2pSession); } } return(null); }
public void Send(P2PMessage p2pMessage, int ackTimeout, AckHandler ackHandler) { ResetTimeoutTimer(); if (p2pBridge == null) { MigrateToOptimalBridge(); } p2pBridge.Send(this, Remote, RemoteContactEndPointID, p2pMessage, ackTimeout, ackHandler); }
public P2PAckMessageEventArgs(P2PMessage p2pMessage, AckHandler ackHandler, int timeoutSec) : base(p2pMessage) { this.ackHandler = ackHandler; this.deleteTick = timeoutSec; if (timeoutSec != 0) { AddSeconds(timeoutSec); } }
/// <summary> /// /// </summary> /// <param name="bridge"></param> /// <param name="p2pMessage"></param> /// <returns></returns> public bool ProcessP2PData(P2PBridge bridge, P2PMessage p2pMessage) { ResetTimeoutTimer(); // Keep track of the remote identifier remoteIdentifier = (p2pMessage.Version == P2PVersion.P2PV2) ? p2pMessage.Header.Identifier + p2pMessage.Header.MessageSize : p2pMessage.Header.Identifier; if (status == P2PSessionStatus.Closed || status == P2PSessionStatus.Error) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("P2PSession {0} received message whilst in '{1}' state", sessionId, status), GetType().Name); return(false); } if (p2pApplication == null) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("P2PSession {0}: Received message for P2P app, but it's either been disposed or not created", sessionId), GetType().Name); return(false); } else { // Application data if (p2pMessage.Header.MessageSize > 0 && p2pMessage.Header.SessionId > 0) { bool reset = false; byte[] appData = new byte[0]; if (p2pMessage.Header.MessageSize == 4 && BitUtility.ToInt32(p2pMessage.InnerBody, 0, true) == 0) { reset = true; } else { appData = new byte[p2pMessage.InnerBody.Length]; Buffer.BlockCopy(p2pMessage.InnerBody, 0, appData, 0, appData.Length); } if (p2pMessage.Version == P2PVersion.P2PV2 && (TFCombination.First == (p2pMessage.V2Header.TFCombination & TFCombination.First))) { reset = true; } return(p2pApplication.ProcessData(bridge, appData, reset)); } return(false); } }
/// <summary> /// Creates an acknowledgement message to this message. /// </summary> /// <returns></returns> public virtual P2PMessage CreateAcknowledgement() { P2PMessage ack = new P2PMessage(Version); ack.Header = Header.CreateAck(); if (Version == P2PVersion.P2PV1) { ack.Footer = Footer; //Keep the same as the message to acknowladge. } return(ack); }
public P2PMessage(P2PMessage message) : this(message.Version) { Header.SessionId = message.Header.SessionId; Header.Identifier = message.Header.Identifier; Header.TotalSize = message.Header.TotalSize; Header.MessageSize = message.Header.MessageSize; Header.AckIdentifier = message.Header.AckIdentifier; if (message.Version == P2PVersion.P2PV1) { V1Header.Offset = message.V1Header.Offset; V1Header.Flags = message.V1Header.Flags; V1Header.AckSessionId = message.V1Header.AckSessionId; V1Header.AckTotalSize = message.V1Header.AckTotalSize; } else if (message.Version == P2PVersion.P2PV2) { V2Header.OperationCode = message.V2Header.OperationCode; V2Header.TFCombination = message.V2Header.TFCombination; V2Header.PackageNumber = message.V2Header.PackageNumber; V2Header.DataRemaining = message.V2Header.DataRemaining; if (message.V2Header.HeaderTLVs.Count > 0) { foreach (KeyValuePair <byte, byte[]> keyvalue in message.V2Header.HeaderTLVs) { V2Header.HeaderTLVs[keyvalue.Key] = keyvalue.Value; } } if (message.V2Header.DataPacketTLVs.Count > 0) { foreach (KeyValuePair <byte, byte[]> keyvalue in message.V2Header.DataPacketTLVs) { V2Header.DataPacketTLVs[keyvalue.Key] = keyvalue.Value; } } } if (message.InnerMessage != null) { InnerMessage = message.InnerMessage; } if (message.InnerBody != null) { InnerBody = message.InnerBody; } Footer = message.Footer; }
public bool Contains(P2PMessage msg) { lock (this) { foreach (P2PSendItem item in this) { if (item.P2PMessage == msg) { return(true); } } } return(false); }
private P2PMessage[] SetSequenceNumberAndRegisterAck(P2PSession session, Contact remote, P2PMessage p2pMessage, AckHandler ackHandler, int timeout) { if (p2pMessage.Header.Identifier == 0) { if (p2pMessage.Version == P2PVersion.P2PV1) { p2pMessage.Header.Identifier = ++sequenceId; } else if (p2pMessage.Version == P2PVersion.P2PV2) { p2pMessage.V2Header.Identifier = sequenceId; } } if (p2pMessage.Version == P2PVersion.P2PV1 && p2pMessage.V1Header.AckSessionId == 0) { p2pMessage.V1Header.AckSessionId = (uint)new Random().Next(50000, int.MaxValue); } if (p2pMessage.Version == P2PVersion.P2PV2 && p2pMessage.V2Header.PackageNumber == 0) { p2pMessage.V2Header.PackageNumber = packageNo; } P2PMessage[] msgs = p2pMessage.SplitMessage(MaxDataSize); if (p2pMessage.Version == P2PVersion.P2PV2) { // Correct local sequence no P2PMessage lastMsg = msgs[msgs.Length - 1]; SequenceId = lastMsg.V2Header.Identifier + lastMsg.V2Header.MessageSize; } if (ackHandler != null) { P2PMessage firstMessage = msgs[0]; RegisterAckHandler(new P2PAckMessageEventArgs(firstMessage, ackHandler, timeout)); } if (session != null) { session.LocalIdentifier = SequenceId; } return(msgs); }
internal P2PMessage WrapSLPMessage(SLPMessage slpMessage) { P2PMessage p2pMessage = new P2PMessage(Version); p2pMessage.InnerMessage = slpMessage; if (Version == P2PVersion.P2PV2) { p2pMessage.V2Header.TFCombination = TFCombination.First; p2pMessage.V2Header.PackageNumber = ++Bridge.PackageNo; } else if (Version == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } return(p2pMessage); }
internal void SendSLPStatus(P2PBridge bridge, P2PMessage msg, Contact dest, Guid destGuid, int code, string phrase) { string target = dest.Account.ToLowerInvariant(); if (msg.Version == P2PVersion.P2PV2) { target += ";" + destGuid.ToString("B"); } SLPMessage slp = new SLPStatusMessage(target, code, phrase); if (msg.IsSLPData) { SLPMessage msgSLP = msg.InnerMessage as SLPMessage; slp.Branch = msgSLP.Branch; slp.CallId = msgSLP.CallId; slp.Source = msgSLP.Target; slp.ContentType = msgSLP.ContentType; if (msgSLP.BodyValues.ContainsKey("SessionID")) { slp.BodyValues["SessionID"] = msgSLP.BodyValues["SessionID"]; } } else { slp.ContentType = "null"; } P2PMessage response = new P2PMessage(msg.Version); response.InnerMessage = slp; if (msg.Version == P2PVersion.P2PV1) { response.V1Header.Flags = P2PFlag.MSNSLPInfo; } else if (msg.Version == P2PVersion.P2PV2) { response.V2Header.OperationCode = (byte)OperationCode.None; response.V2Header.TFCombination = TFCombination.First; } bridge.Send(null, dest, destGuid, response); }
/// <summary> /// Closes the session. /// </summary> public void Close() { if (status == P2PSessionStatus.Closing) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("P2PSession {0} was already closing, forcing unclean closure", SessionId), GetType().Name); OnClosed(new ContactEventArgs(Local)); } else { OnClosing(new ContactEventArgs(Local)); SLPRequestMessage slpMessage = new SLPRequestMessage(RemoteContactEPIDString, "BYE"); slpMessage.Target = RemoteContactEPIDString; slpMessage.Source = LocalContactEPIDString; slpMessage.Branch = invitation.Branch; slpMessage.CallId = invitation.CallId; slpMessage.MaxForwards = 0; slpMessage.ContentType = "application/x-msnmsgr-sessionclosebody"; slpMessage.BodyValues["SessionID"] = SessionId.ToString(System.Globalization.CultureInfo.InvariantCulture); P2PMessage p2pMessage = new P2PMessage(Version); if (version == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } else if (version == P2PVersion.P2PV2) { p2pMessage.V2Header.OperationCode = (byte)OperationCode.RAK; p2pMessage.V2Header.TFCombination = TFCombination.First; p2pMessage.V2Header.PackageNumber = ++p2pBridge.PackageNo; } p2pMessage.InnerMessage = slpMessage; Send(p2pMessage, delegate(P2PMessage ack) { OnClosed(new ContactEventArgs(Local)); }); } }
/// <summary> /// Sends invitation message if initiated locally. Sets remote identifiers after ack received. /// </summary> public void Invite() { if (status != P2PSessionStatus.WaitingForRemote) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("Invite called, but we're not waiting for the remote client (State {0})", status), GetType().Name); } else { // Get id from bridge.... { MigrateToOptimalBridge(); localBaseIdentifier = p2pBridge.SequenceId; localIdentifier = localBaseIdentifier; } P2PMessage p2pMessage = WrapSLPMessage(invitation); if (version == P2PVersion.P2PV2) { if (p2pBridge.Synced == false) { p2pMessage.V2Header.OperationCode = (byte)(OperationCode.SYN | OperationCode.RAK); p2pMessage.V2Header.AppendPeerInfoTLV(); Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("{0} invitation sending with SYN+RAK op...", SessionId), GetType().Name); } } p2pMessage.InnerMessage = invitation; Send(p2pMessage, delegate(P2PMessage ack) { remoteBaseIdentifier = ack.Header.Identifier; remoteIdentifier = remoteBaseIdentifier; Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("{0} invitation sent with SYN+RAK op and received RemoteBaseIdentifier is: {1}", SessionId, remoteIdentifier), GetType().Name); }); } }
public P2PMessage[] CreateFromOffsets(long[] offsets, byte[] allData) { List <P2PMessage> messages = new List <P2PMessage>(offsets.Length); long offset = allData.Length; for (int i = offsets.Length - 1; i >= 0; i--) { P2PMessage m = new P2PMessage(Version); long length = offset - offsets[i]; byte[] part = new byte[length]; Array.Copy(allData, offsets[i], part, 0, length); m.ParseBytes(part); offset -= length; messages.Add(m); } messages.Reverse(); return(messages.ToArray()); }
public static Guid CreateGuidFromData(P2PVersion ver, byte[] data) { Guid ret = Guid.Empty; if (ver == P2PVersion.P2PV1) { P2PMessage message = new P2PMessage(ver); message.ParseBytes(data); ret = new Guid( (int)message.V1Header.AckSessionId, (short)(message.Header.AckIdentifier & 0x0000FFFF), (short)((message.Header.AckIdentifier & 0xFFFF0000) >> 16), (byte)((message.V1Header.AckTotalSize & 0x00000000000000FF)), (byte)((message.V1Header.AckTotalSize & 0x000000000000FF00) >> 8), (byte)((message.V1Header.AckTotalSize & 0x0000000000FF0000) >> 16), (byte)((message.V1Header.AckTotalSize & 0x00000000FF000000) >> 24), (byte)((message.V1Header.AckTotalSize & 0x000000FF00000000) >> 32), (byte)((message.V1Header.AckTotalSize & 0x0000FF0000000000) >> 40), (byte)((message.V1Header.AckTotalSize & 0x00FF000000000000) >> 48), (byte)((message.V1Header.AckTotalSize & 0xFF00000000000000) >> 56) ); } else if (ver == P2PVersion.P2PV2) { Int32 a = BitUtility.ToInt32(data, 0, BitConverter.IsLittleEndian); Int16 b = BitUtility.ToInt16(data, 4, BitConverter.IsLittleEndian); Int16 c = BitUtility.ToInt16(data, 6, BitConverter.IsLittleEndian); byte d = data[8], e = data[9], f = data[10], g = data[11]; byte h = data[12], i = data[13], j = data[14], k = data[15]; ret = new Guid(a, b, c, d, e, f, g, h, i, j, k); } return ret; }
/// <summary> /// Rejects the received invitation. /// </summary> public void Decline() { if (status != P2PSessionStatus.WaitingForLocal) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("Declined called, but we're not waiting for the local client (State {0})", status), GetType().Name); } else { Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("{0} declined", SessionId), GetType().Name); SLPStatusMessage slpMessage = new SLPStatusMessage(RemoteContactEPIDString, 603, "Decline"); slpMessage.Target = RemoteContactEPIDString; slpMessage.Source = LocalContactEPIDString; slpMessage.Branch = invitation.Branch; slpMessage.CallId = invitation.CallId; slpMessage.CSeq = 1; slpMessage.ContentType = "application/x-msnmsgr-sessionreqbody"; slpMessage.BodyValues["SessionID"] = SessionId.ToString(System.Globalization.CultureInfo.InvariantCulture); P2PMessage p2pMessage = new P2PMessage(Version); if (version == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } else if (version == P2PVersion.P2PV2) { p2pMessage.V2Header.OperationCode = (byte)OperationCode.RAK; p2pMessage.V2Header.TFCombination = TFCombination.First; p2pMessage.V2Header.PackageNumber = ++Bridge.PackageNo; } p2pMessage.InnerMessage = slpMessage; Send(p2pMessage); Close(); } }
public void Remove(P2PMessage msg) { lock (this) { P2PSendItem?removeItem = null; foreach (P2PSendItem item in this) { if (item.P2PMessage == msg) { removeItem = item; break; } } if (removeItem != null) { Remove(removeItem.Value); return; } } throw new ArgumentException("msg not found in queue"); }
protected override void SendMultiPacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage[] sendList) { if (remote == null) return; NSMessageProcessor nsmp = (NSMessageProcessor)NSMessageHandler.MessageProcessor; string to = ((int)remote.ClientType).ToString() + ":" + remote.Account; string from = ((int)NSMessageHandler.Owner.ClientType).ToString() + ":" + NSMessageHandler.Owner.Account; MultiMimeMessage mmMessage = new MultiMimeMessage(to, from); mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.EPID] = remoteGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "PE"; mmMessage.RoutingHeaders[MIMERoutingHeaders.Options] = "0"; mmMessage.ContentKeyVersion = "2.0"; mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/x-msnmsgrp2p"; mmMessage.ContentHeaders[MIMEContentHeaders.ContentTransferEncoding] = "binary"; mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Data; List<string> bridgingOffsets = new List<string>(); List<object> userStates = new List<object>(); byte[] buffer = new byte[0]; foreach (P2PMessage p2pMessage in sendList) { SLPMessage slpMessage = p2pMessage.IsSLPData ? p2pMessage.InnerMessage as SLPMessage : null; if (slpMessage != null && ((slpMessage.ContentType == "application/x-msnmsgr-transreqbody" || slpMessage.ContentType == "application/x-msnmsgr-transrespbody" || slpMessage.ContentType == "application/x-msnmsgr-transdestaddrupdate"))) { SendOnePacket(session, remote, remoteGuid, p2pMessage); } else { bridgingOffsets.Add(buffer.Length.ToString()); buffer = NetworkMessage.AppendArray(buffer, p2pMessage.GetBytes(true)); int transId = nsmp.IncreaseTransactionID(); userStates.Add(transId); lock (p2pAckMessages) p2pAckMessages[transId] = new P2PMessageSessionEventArgs(p2pMessage, session); //mmMessage.ContentHeaders[MIMEContentHeaders.Pipe] = PackageNo.ToString(); } } if (buffer.Length > 0) { mmMessage.ContentHeaders[MIMEContentHeaders.BridgingOffsets] = String.Join(",", bridgingOffsets.ToArray()); mmMessage.InnerBody = buffer; int transId2 = nsmp.IncreaseTransactionID(); userStates.Add(transId2); NSMessage sdgPayload = new NSMessage("SDG"); sdgPayload.TransactionID = transId2; sdgPayload.InnerMessage = mmMessage; nsmp.Processor.Send(sdgPayload.GetBytes(), userStates.ToArray()); } }
public void Enqueue(Contact remote, Guid remoteGuid, P2PMessage message) { base.Enqueue(new P2PSendItem(remote, remoteGuid, message)); }
/// <summary> /// Send a P2P Message to the specified P2PSession. /// </summary> /// <param name="session"> /// The application layer, which is a <see cref="P2PSession"/> /// </param> /// <param name="remote"> /// he receiver <see cref="Contact"/> /// </param> /// <param name="remoteGuid"> /// A <see cref="Guid"/> /// </param> /// <param name="msg"> /// he <see cref="P2PMessage"/> to be sent. /// </param> /// <param name="ackTimeout"> /// The maximum time to wait for an ACK. <see cref="System.Int32"/> /// </param> /// <param name="ackHandler"> /// The <see cref="AckHandler"/> to handle the ACK. /// </param> public virtual void Send(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage msg, int ackTimeout, AckHandler ackHandler) { if (remote == null) throw new ArgumentNullException("remote"); P2PMessage[] msgs = SetSequenceNumberAndRegisterAck(session, remote, msg, ackHandler, ackTimeout); if (session == null) { if (!IsOpen) { Trace.WriteLineIf(Settings.TraceSwitch.TraceError, "Send called with no session on a closed bridge", GetType().Name); return; } // Bypass queueing foreach (P2PMessage m in msgs) { SendOnePacket(null, remote, remoteGuid, m); } return; } if (!SuitableFor(session)) { Trace.WriteLineIf(Settings.TraceSwitch.TraceError, "Send called with a session this bridge is not suitable for", GetType().Name); return; } lock (sendQueues) { if (!sendQueues.ContainsKey(session)) sendQueues[session] = new P2PSendQueue(); } lock (sendQueues[session]) { foreach (P2PMessage m in msgs) sendQueues[session].Enqueue(remote, remoteGuid, m); } ProcessSendQueues(); }
public P2PDataMessage(P2PMessage copy) : base(copy) { }
internal bool HandleRAK(Contact source, Guid sourceGuid, P2PMessage msg) { bool requireAck = false; if (msg.Header.RequireAck) { requireAck = true; P2PMessage ack = msg.CreateAcknowledgement(); if (ack.Header.RequireAck) { // SYN Send(null, source, sourceGuid, ack, DefaultTimeout, delegate(P2PMessage sync) { SyncId = sync.Header.AckIdentifier; Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("SYNC completed for: {0}", this), GetType().Name); }); } else { // ACK Send(null, source, sourceGuid, ack); } } return requireAck; }
private static void ProcessDCRespInvite(SLPMessage message, NSMessageHandler ns, P2PSession startupSession) { MimeDictionary bodyValues = message.BodyValues; // Check the protocol if (bodyValues.ContainsKey("Bridge") && bodyValues["Bridge"].ToString() == "TCPv1" && bodyValues.ContainsKey("Listening") && bodyValues["Listening"].ToString().ToLowerInvariant().IndexOf("true") >= 0) { Contact remote = ns.ContactList.GetContactWithCreate(message.FromEmailAccount, IMAddressInfoType.WindowsLive); Guid remoteGuid = message.FromEndPoint; DCNonceType dcNonceType; Guid remoteNonce = ParseDCNonce(message.BodyValues, out dcNonceType); bool hashed = (dcNonceType == DCNonceType.Sha1); Guid replyGuid = hashed ? remote.dcPlainKey : remoteNonce; IPEndPoint[] selectedPoint = SelectIPEndPoint(bodyValues, ns); if (selectedPoint != null && selectedPoint.Length > 0) { P2PVersion ver = message.P2PVersion; // We must connect to the remote client ConnectivitySettings settings = new ConnectivitySettings(); settings.EndPoints = selectedPoint; remote.DirectBridge = CreateDirectConnection(remote, remoteGuid, ver, settings, replyGuid, remoteNonce, hashed, ns, startupSession); bool needConnectingEndpointInfo; if (bodyValues.ContainsKey("NeedConnectingEndpointInfo") && bool.TryParse(bodyValues["NeedConnectingEndpointInfo"], out needConnectingEndpointInfo) && needConnectingEndpointInfo == true) { IPEndPoint ipep = ((TCPv1Bridge)remote.DirectBridge).LocalEndPoint; string desc = "stroPdnAsrddAlanretnI4vPI"; char[] rev = ipep.ToString().ToCharArray(); Array.Reverse(rev); string ipandport = new string(rev); SLPRequestMessage slpResponseMessage = new SLPRequestMessage(message.Source, MSNSLPRequestMethod.ACK); slpResponseMessage.Source = message.Target; slpResponseMessage.Via = message.Via; slpResponseMessage.CSeq = 0; slpResponseMessage.CallId = Guid.Empty; slpResponseMessage.MaxForwards = 0; slpResponseMessage.ContentType = @"application/x-msnmsgr-transdestaddrupdate"; slpResponseMessage.BodyValues[desc] = ipandport; slpResponseMessage.BodyValues["Nat-Trav-Msg-Type"] = "WLX-Nat-Trav-Msg-Updated-Connecting-Port"; P2PMessage msg = new P2PMessage(ver); msg.InnerMessage = slpResponseMessage; ns.SDGBridge.Send(null, remote, remoteGuid, msg); } return; } } if (startupSession != null) startupSession.DirectNegotiationFailed(); }
/// <summary> /// Split big P2PMessages to transport over sb or dc. /// </summary> /// <param name="maxSize"></param> /// <returns></returns> public P2PMessage[] SplitMessage(int maxSize) { uint payloadMessageSize = 0; if (Version == P2PVersion.P2PV1) { payloadMessageSize = V1Header.MessageSize; } if (Version == P2PVersion.P2PV2) { payloadMessageSize = (uint)V2Header.MessageSize - (uint)V2Header.DataPacketHeaderLength; } if (payloadMessageSize <= maxSize) { return new P2PMessage[] { this } } ; List <P2PMessage> chunks = new List <P2PMessage>(); byte[] totalMessage = (InnerBody != null) ? InnerBody : InnerMessage.GetBytes(); long offset = 0; if (Version == P2PVersion.P2PV1) { while (offset < totalMessage.LongLength) { P2PMessage chunkMessage = new P2PMessage(Version); uint messageSize = (uint)Math.Min((uint)maxSize, (totalMessage.LongLength - offset)); byte[] chunk = new byte[messageSize]; Buffer.BlockCopy(totalMessage, (int)offset, chunk, 0, (int)messageSize); chunkMessage.V1Header.Flags = V1Header.Flags; chunkMessage.V1Header.AckIdentifier = V1Header.AckIdentifier; chunkMessage.V1Header.AckTotalSize = V1Header.AckTotalSize; chunkMessage.V1Header.Identifier = V1Header.Identifier; chunkMessage.V1Header.SessionId = V1Header.SessionId; chunkMessage.V1Header.TotalSize = V1Header.TotalSize; chunkMessage.V1Header.Offset = (ulong)offset; chunkMessage.V1Header.MessageSize = messageSize; chunkMessage.InnerBody = chunk; chunkMessage.V1Header.AckSessionId = V1Header.AckSessionId; chunkMessage.Footer = Footer; chunkMessage.PrepareMessage(); chunks.Add(chunkMessage); offset += messageSize; } } if (Version == P2PVersion.P2PV2) { uint nextId = Header.Identifier; long dataRemain = (long)V2Header.DataRemaining; while (offset < totalMessage.LongLength) { P2PMessage chunkMessage = new P2PMessage(Version); int maxDataSize = maxSize; if (offset == 0 && V2Header.HeaderTLVs.Count > 0) { foreach (KeyValuePair <byte, byte[]> keyvalue in V2Header.HeaderTLVs) { chunkMessage.V2Header.HeaderTLVs[keyvalue.Key] = keyvalue.Value; } maxDataSize = maxSize - chunkMessage.V2Header.HeaderLength; } uint dataSize = (uint)Math.Min((uint)maxDataSize, (totalMessage.LongLength - offset)); byte[] chunk = new byte[dataSize]; Buffer.BlockCopy(totalMessage, (int)offset, chunk, 0, (int)dataSize); if (offset == 0) { chunkMessage.V2Header.OperationCode = V2Header.OperationCode; } chunkMessage.V2Header.SessionId = V2Header.SessionId; chunkMessage.V2Header.TFCombination = V2Header.TFCombination; chunkMessage.V2Header.PackageNumber = V2Header.PackageNumber; if (totalMessage.LongLength + dataRemain - (dataSize + offset) > 0) { chunkMessage.V2Header.DataRemaining = (ulong)(totalMessage.LongLength + dataRemain - (dataSize + offset)); } if ((offset != 0) && TFCombination.First == (V2Header.TFCombination & TFCombination.First)) { chunkMessage.V2Header.TFCombination = (TFCombination)(V2Header.TFCombination - TFCombination.First); } chunkMessage.InnerBody = chunk; chunkMessage.Header.Identifier = nextId; nextId += chunkMessage.Header.MessageSize; chunks.Add(chunkMessage); offset += dataSize; } } return(chunks.ToArray()); }
/// <summary> /// Sends a P2P message. /// </summary> /// <param name="p2pMessage"></param> public void SendMessage(P2PMessage p2pMessage) { SendMessage(p2pMessage, 0, null); }
/// <summary> /// Sends a message with the specified <see cref="P2PMessage"/> and <see cref="AckHandler"/>. /// </summary> public virtual void SendMessage(P2PMessage p2pMessage, int ackTimeout, AckHandler ackHandler) { Debug.Assert(p2pMessage.Version == version); p2pMessage.Header.SessionId = p2pSession.SessionId; // If not an ack, set the footer (p2pv1 only) if (p2pMessage.Version == P2PVersion.P2PV1 && (p2pMessage.V1Header.Flags & P2PFlag.Acknowledgement) != P2PFlag.Acknowledgement) p2pMessage.Footer = ApplicationId; p2pSession.Send(p2pMessage, ackTimeout, ackHandler); }
private P2PMessage[] SetSequenceNumberAndRegisterAck(P2PSession session, Contact remote, P2PMessage p2pMessage, AckHandler ackHandler, int timeout) { if (p2pMessage.Header.Identifier == 0) { if (p2pMessage.Version == P2PVersion.P2PV1) { p2pMessage.Header.Identifier = ++sequenceId; } else if (p2pMessage.Version == P2PVersion.P2PV2) { p2pMessage.V2Header.Identifier = sequenceId; } } if (p2pMessage.Version == P2PVersion.P2PV1 && p2pMessage.V1Header.AckSessionId == 0) { p2pMessage.V1Header.AckSessionId = (uint)new Random().Next(50000, int.MaxValue); } if (p2pMessage.Version == P2PVersion.P2PV2 && p2pMessage.V2Header.PackageNumber == 0) { p2pMessage.V2Header.PackageNumber = packageNo; } P2PMessage[] msgs = p2pMessage.SplitMessage(MaxDataSize); if (p2pMessage.Version == P2PVersion.P2PV2) { // Correct local sequence no P2PMessage lastMsg = msgs[msgs.Length - 1]; SequenceId = lastMsg.V2Header.Identifier + lastMsg.V2Header.MessageSize; } if (ackHandler != null) { P2PMessage firstMessage = msgs[0]; RegisterAckHandler(new P2PAckMessageEventArgs(firstMessage, ackHandler, timeout)); } if (session != null) { session.LocalIdentifier = SequenceId; } return msgs; }
protected abstract void SendOnePacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage msg);
internal void SendSLPStatus(P2PBridge bridge, P2PMessage msg, Contact dest, Guid destGuid, int code, string phrase) { string target = dest.Account.ToLowerInvariant(); if (msg.Version == P2PVersion.P2PV2) { target += ";" + destGuid.ToString("B"); } SLPMessage slp = new SLPStatusMessage(target, code, phrase); if (msg.IsSLPData) { SLPMessage msgSLP = msg.InnerMessage as SLPMessage; slp.Branch = msgSLP.Branch; slp.CallId = msgSLP.CallId; slp.Source = msgSLP.Target; slp.ContentType = msgSLP.ContentType; if (msgSLP.BodyValues.ContainsKey("SessionID")) { slp.BodyValues["SessionID"] = msgSLP.BodyValues["SessionID"]; } } else slp.ContentType = "null"; P2PMessage response = new P2PMessage(msg.Version); response.InnerMessage = slp; if (msg.Version == P2PVersion.P2PV1) { response.V1Header.Flags = P2PFlag.MSNSLPInfo; } else if (msg.Version == P2PVersion.P2PV2) { response.V2Header.OperationCode = (byte)OperationCode.None; response.V2Header.TFCombination = TFCombination.First; } bridge.Send(null, dest, destGuid, response); }
protected override void SendOnePacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage p2pMessage) { if (remote == null) { return; } string to = ((int)remote.ClientType).ToString() + ":" + remote.Account; string from = ((int)NSMessageHandler.Owner.ClientType).ToString() + ":" + NSMessageHandler.Owner.Account; MultiMimeMessage mmMessage = new MultiMimeMessage(to, from); mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.EPID] = remoteGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "PE"; mmMessage.RoutingHeaders[MIMERoutingHeaders.Options] = "0"; mmMessage.ContentKeyVersion = "2.0"; SLPMessage slpMessage = p2pMessage.IsSLPData ? p2pMessage.InnerMessage as SLPMessage : null; if (slpMessage != null && ((slpMessage.ContentType == "application/x-msnmsgr-transreqbody" || slpMessage.ContentType == "application/x-msnmsgr-transrespbody" || slpMessage.ContentType == "application/x-msnmsgr-transdestaddrupdate"))) { mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.SignalP2P; mmMessage.InnerBody = slpMessage.GetBytes(false); mmMessage.InnerMessage = slpMessage; } else { mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/x-msnmsgrp2p"; mmMessage.ContentHeaders[MIMEContentHeaders.ContentTransferEncoding] = "binary"; mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Data; //mmMessage.ContentHeaders[MIMEContentHeaders.Pipe] = PackageNo.ToString(); mmMessage.ContentHeaders[MIMEContentHeaders.BridgingOffsets] = "0"; mmMessage.InnerBody = p2pMessage.GetBytes(true); mmMessage.InnerMessage = p2pMessage; } NSMessageProcessor nsmp = (NSMessageProcessor)NSMessageHandler.MessageProcessor; int transId = nsmp.IncreaseTransactionID(); lock (p2pAckMessages) p2pAckMessages[transId] = new P2PMessageSessionEventArgs(p2pMessage, session); NSMessage sdgPayload = new NSMessage("SDG"); sdgPayload.TransactionID = transId; sdgPayload.InnerMessage = mmMessage; nsmp.SendMessage(sdgPayload, sdgPayload.TransactionID); }
protected override void SendOnePacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage p2pMessage) { if (remote == null) return; string to = ((int)remote.ClientType).ToString() + ":" + remote.Account; string from = ((int)NSMessageHandler.Owner.ClientType).ToString() + ":" + NSMessageHandler.Owner.Account; MultiMimeMessage mmMessage = new MultiMimeMessage(to, from); mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.EPID] = remoteGuid.ToString("B").ToLowerInvariant(); mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "PE"; mmMessage.RoutingHeaders[MIMERoutingHeaders.Options] = "0"; mmMessage.ContentKeyVersion = "2.0"; SLPMessage slpMessage = p2pMessage.IsSLPData ? p2pMessage.InnerMessage as SLPMessage : null; if (slpMessage != null && ((slpMessage.ContentType == "application/x-msnmsgr-transreqbody" || slpMessage.ContentType == "application/x-msnmsgr-transrespbody" || slpMessage.ContentType == "application/x-msnmsgr-transdestaddrupdate"))) { mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.SignalP2P; mmMessage.InnerBody = slpMessage.GetBytes(false); mmMessage.InnerMessage = slpMessage; } else { mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/x-msnmsgrp2p"; mmMessage.ContentHeaders[MIMEContentHeaders.ContentTransferEncoding] = "binary"; mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Data; //mmMessage.ContentHeaders[MIMEContentHeaders.Pipe] = PackageNo.ToString(); mmMessage.ContentHeaders[MIMEContentHeaders.BridgingOffsets] = "0"; mmMessage.InnerBody = p2pMessage.GetBytes(true); mmMessage.InnerMessage = p2pMessage; } NSMessageProcessor nsmp = (NSMessageProcessor)NSMessageHandler.MessageProcessor; int transId = nsmp.IncreaseTransactionID(); lock (p2pAckMessages) p2pAckMessages[transId] = new P2PMessageSessionEventArgs(p2pMessage, session); NSMessage sdgPayload = new NSMessage("SDG"); sdgPayload.TransactionID = transId; sdgPayload.InnerMessage = mmMessage; nsmp.SendMessage(sdgPayload, sdgPayload.TransactionID); }
internal bool SendDirectInvite() { // Skip if we're currently using a TCPBridge. if (Remote.DirectBridge != null && Remote.DirectBridge.IsOpen) return false; int netId; string connectionType = ConnectionType(nsMessageHandler, out netId); P2PMessage p2pMessage = new P2PMessage(Version); Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, String.Format("Connection type set to {0} for session {1}", connectionType, SessionId.ToString())); // Create the message SLPRequestMessage slpMessage = new SLPRequestMessage(RemoteContactEPIDString, MSNSLPRequestMethod.INVITE); slpMessage.Source = LocalContactEPIDString; slpMessage.CSeq = 0; slpMessage.CallId = Invitation.CallId; slpMessage.MaxForwards = 0; slpMessage.ContentType = "application/x-msnmsgr-transreqbody"; slpMessage.BodyValues["Bridges"] = "TCPv1 SBBridge"; slpMessage.BodyValues["Capabilities-Flags"] = "1"; slpMessage.BodyValues["NetID"] = netId.ToString(CultureInfo.InvariantCulture); slpMessage.BodyValues["Conn-Type"] = connectionType; slpMessage.BodyValues["TCP-Conn-Type"] = connectionType; slpMessage.BodyValues["UPnPNat"] = "false"; // UPNP Enabled slpMessage.BodyValues["ICF"] = (connectionType == "Firewall").ToString(); // Firewall enabled slpMessage.BodyValues["Nat-Trav-Msg-Type"] = "WLX-Nat-Trav-Msg-Direct-Connect-Req"; // We support Hashed-Nonce ( 2 way handshake ) Remote.GenerateNewDCKeys(); slpMessage.BodyValues["Hashed-Nonce"] = Remote.dcLocalHashedNonce.ToString("B").ToUpper(CultureInfo.InvariantCulture); p2pMessage.InnerMessage = slpMessage; if (p2pMessage.Version == P2PVersion.P2PV2) { p2pMessage.V2Header.TFCombination = TFCombination.First; } else if (p2pMessage.Version == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } // These 3 step is very important... // 1- Stop sending for this session on sdgbridge until we receive a response to the direct invite or the timeout expires nsMessageHandler.SDGBridge.StopSending(this); // 2- Setup a dc timer. SetupDCTimer(); // 3- Don't pass p2psession to the sdg bridge. Because, sdgbridge stopped this session. nsMessageHandler.SDGBridge.Send(null /*must be null to bypass queueing*/, Remote, RemoteContactEndPointID, p2pMessage); return true; }
/// <summary> /// Copy constructor. Creates a shallow copy of the properties of the P2PMessage. /// </summary> /// <param name="message"></param> public P2PDCMessage(P2PMessage message) : base(message) { }
private static void ProcessDCReqInvite(SLPMessage message, NSMessageHandler ns, P2PSession startupSession) { if (startupSession != null && startupSession.Bridge != null && startupSession.Bridge is TCPv1Bridge) { return; // We are using a dc bridge already. Don't allow second one. } if (message.BodyValues.ContainsKey("Bridges") && message.BodyValues["Bridges"].ToString().Contains("TCPv1")) { SLPStatusMessage slpMessage = new SLPStatusMessage(message.Source, 200, "OK"); slpMessage.Target = message.Source; slpMessage.Source = message.Target; slpMessage.Branch = message.Branch; slpMessage.CSeq = 1; slpMessage.CallId = message.CallId; slpMessage.MaxForwards = 0; slpMessage.ContentType = "application/x-msnmsgr-transrespbody"; slpMessage.BodyValues["Bridge"] = "TCPv1"; Guid remoteGuid = message.FromEndPoint; Contact remote = ns.ContactList.GetContactWithCreate(message.FromEmailAccount, IMAddressInfoType.WindowsLive); DCNonceType dcNonceType; Guid remoteNonce = ParseDCNonce(message.BodyValues, out dcNonceType); if (remoteNonce == Guid.Empty) // Plain remoteNonce = remote.dcPlainKey; bool hashed = (dcNonceType == DCNonceType.Sha1); string nonceFieldName = hashed ? "Hashed-Nonce" : "Nonce"; Guid myHashedNonce = hashed ? remote.dcLocalHashedNonce : remoteNonce; Guid myPlainNonce = remote.dcPlainKey; if (dcNonceType == DCNonceType.Sha1) { // Remote contact supports Hashed-Nonce remote.dcType = dcNonceType; remote.dcRemoteHashedNonce = remoteNonce; } else { remote.dcType = DCNonceType.Plain; myPlainNonce = remote.dcPlainKey = remote.dcLocalHashedNonce = remote.dcRemoteHashedNonce = remoteNonce; } // Find host by name IPAddress ipAddress = ns.LocalEndPoint.Address; int port; P2PVersion ver = message.P2PVersion; if (Settings.DisableP2PDirectConnections || false == ipAddress.Equals(ns.ExternalEndPoint.Address) || (0 == (port = GetNextDirectConnectionPort(ipAddress)))) { slpMessage.BodyValues["Listening"] = "false"; slpMessage.BodyValues[nonceFieldName] = Guid.Empty.ToString("B").ToUpper(CultureInfo.InvariantCulture); } else { // Let's listen remote.DirectBridge = ListenForDirectConnection(remote, remoteGuid, ns, ver, startupSession, ipAddress, port, myPlainNonce, remoteNonce, hashed); slpMessage.BodyValues["Listening"] = "true"; slpMessage.BodyValues["Capabilities-Flags"] = "1"; slpMessage.BodyValues["IPv6-global"] = string.Empty; slpMessage.BodyValues["Nat-Trav-Msg-Type"] = "WLX-Nat-Trav-Msg-Direct-Connect-Resp"; slpMessage.BodyValues["UPnPNat"] = "false"; slpMessage.BodyValues["NeedConnectingEndpointInfo"] = "true"; slpMessage.BodyValues["Conn-Type"] = "Direct-Connect"; slpMessage.BodyValues["TCP-Conn-Type"] = "Direct-Connect"; slpMessage.BodyValues[nonceFieldName] = myHashedNonce.ToString("B").ToUpper(CultureInfo.InvariantCulture); slpMessage.BodyValues["IPv4Internal-Addrs"] = ipAddress.ToString(); slpMessage.BodyValues["IPv4Internal-Port"] = port.ToString(CultureInfo.InvariantCulture); // check if client is behind firewall (NAT-ted) // if so, send the public ip also the client, so it can try to connect to that ip if (!ns.ExternalEndPoint.Address.Equals(ns.LocalEndPoint.Address)) { slpMessage.BodyValues["IPv4External-Addrs"] = ns.ExternalEndPoint.Address.ToString(); slpMessage.BodyValues["IPv4External-Port"] = port.ToString(CultureInfo.InvariantCulture); } } P2PMessage p2pMessage = new P2PMessage(ver); p2pMessage.InnerMessage = slpMessage; if (ver == P2PVersion.P2PV2) { p2pMessage.V2Header.TFCombination = TFCombination.First; } else if (ver == P2PVersion.P2PV1) { p2pMessage.V1Header.Flags = P2PFlag.MSNSLPInfo; } if (startupSession != null) { startupSession.SetupDCTimer(); startupSession.Bridge.Send(null, startupSession.Remote, startupSession.RemoteContactEndPointID, p2pMessage); } else { ns.SDGBridge.Send(null, remote, remoteGuid, p2pMessage); } } else { if (startupSession != null) startupSession.DirectNegotiationFailed(); } }
public P2PMessage(P2PMessage message) : this(message.Version) { Header.SessionId = message.Header.SessionId; Header.Identifier = message.Header.Identifier; Header.TotalSize = message.Header.TotalSize; Header.MessageSize = message.Header.MessageSize; Header.AckIdentifier = message.Header.AckIdentifier; if (message.Version == P2PVersion.P2PV1) { V1Header.Offset = message.V1Header.Offset; V1Header.Flags = message.V1Header.Flags; V1Header.AckSessionId = message.V1Header.AckSessionId; V1Header.AckTotalSize = message.V1Header.AckTotalSize; } else if (message.Version == P2PVersion.P2PV2) { V2Header.OperationCode = message.V2Header.OperationCode; V2Header.TFCombination = message.V2Header.TFCombination; V2Header.PackageNumber = message.V2Header.PackageNumber; V2Header.DataRemaining = message.V2Header.DataRemaining; if (message.V2Header.HeaderTLVs.Count > 0) { foreach (KeyValuePair<byte, byte[]> keyvalue in message.V2Header.HeaderTLVs) { V2Header.HeaderTLVs[keyvalue.Key] = keyvalue.Value; } } if (message.V2Header.DataPacketTLVs.Count > 0) { foreach (KeyValuePair<byte, byte[]> keyvalue in message.V2Header.DataPacketTLVs) { V2Header.DataPacketTLVs[keyvalue.Key] = keyvalue.Value; } } } if (message.InnerMessage != null) InnerMessage = message.InnerMessage; if (message.InnerBody != null) InnerBody = message.InnerBody; Footer = message.Footer; }
protected virtual void SendMultiPacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage[] sendList) { foreach (P2PMessage p2pMessage in sendList) { SendOnePacket(session, remote, remoteGuid, p2pMessage); } }
protected override void SendOnePacket(P2PSession session, Contact remote, Guid remoteGuid, P2PMessage msg) { directConnection.SendMessage(msg, new P2PMessageSessionEventArgs(msg, session)); }
public void Send(P2PMessage p2pMessage, AckHandler ackHandler) { Send(p2pMessage, P2PBridge.DefaultTimeout, ackHandler); }
/// <summary> /// Buffers incompleted P2PMessage SLP messages. Ignores data and control messages. /// </summary> /// <param name="p2pMessage"></param> /// <returns> /// true if the P2PMessage is buffering (not completed) or invalid packet received; /// false if the p2p message fully buffered or no need to buffer. /// </returns> public bool BufferMessage(ref P2PMessage p2pMessage) { // P2PV1 and P2PV2 check if (p2pMessage.Header.MessageSize == 0 || // Ack message or Unsplitted p2pMessage.Header.SessionId > 0) // Data message { return false; // No need to buffer } // P2PV2 pooling if (p2pMessage.Version == P2PVersion.P2PV2) { if ((p2pMessage.V2Header.TFCombination == TFCombination.First && p2pMessage.V2Header.DataRemaining == 0) || // Unsplitted SLP message or data preparation message (p2pMessage.V2Header.TFCombination > TFCombination.First)) // Data message { return false; // No need to buffer } // First splitted SLP message. if (p2pMessage.V2Header.TFCombination == TFCombination.First && p2pMessage.V2Header.DataRemaining > 0) { P2PMessage totalMessage = new P2PMessage(p2pMessage); // Copy it ulong totalSize = (ulong)(p2pMessage.V2Header.MessageSize - p2pMessage.V2Header.DataPacketHeaderLength) + p2pMessage.V2Header.DataRemaining; totalMessage.InnerBody = new byte[totalSize]; // Allocate buffer as needed Array.Copy(p2pMessage.InnerBody, 0, totalMessage.InnerBody, (long)0, (long)p2pMessage.InnerBody.Length); lock (incompletedP2PV2Messages) incompletedP2PV2Messages[p2pMessage.V2Header.Identifier + p2pMessage.V2Header.MessageSize] = totalMessage; return true; // Buffering } // Other splitted SLP messages if (p2pMessage.V2Header.TFCombination == TFCombination.None) { lock (incompletedP2PV2Messages) { if (incompletedP2PV2Messages.ContainsKey(p2pMessage.V2Header.Identifier)) { if (incompletedP2PV2Messages[p2pMessage.V2Header.Identifier].V2Header.PackageNumber == p2pMessage.V2Header.PackageNumber) { P2PMessage totalMessage = incompletedP2PV2Messages[p2pMessage.V2Header.Identifier]; ulong dataSize = Math.Min(((ulong)(p2pMessage.V2Header.MessageSize - p2pMessage.V2Header.DataPacketHeaderLength)), totalMessage.V2Header.DataRemaining); ulong offSet = ((ulong)totalMessage.InnerBody.LongLength) - totalMessage.V2Header.DataRemaining; // Check range and buffer overflow... if (((p2pMessage.V2Header.DataRemaining + (ulong)dataSize) == totalMessage.V2Header.DataRemaining) && (ulong)(dataSize + offSet + p2pMessage.V2Header.DataRemaining) == (ulong)totalMessage.InnerBody.LongLength) { Array.Copy(p2pMessage.InnerBody, 0, totalMessage.InnerBody, (long)offSet, (long)dataSize); uint originalIdentifier = p2pMessage.V2Header.Identifier; uint newIdentifier = p2pMessage.V2Header.Identifier + p2pMessage.V2Header.MessageSize; totalMessage.V2Header.DataRemaining = p2pMessage.V2Header.DataRemaining; totalMessage.V2Header.Identifier = newIdentifier; if (originalIdentifier != newIdentifier) { incompletedP2PV2Messages.Remove(originalIdentifier); } // Don't debug p2p inner packet (SLP) here. Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, " Buffering splitted SLP message! DataRemaining:" + totalMessage.V2Header.DataRemaining + " NewIdentifier: " + newIdentifier); if (p2pMessage.V2Header.DataRemaining > 0) { // Don't debug p2p packet here. Because it hasn't completed yet and SLPMessage.Parse() fails... incompletedP2PV2Messages[newIdentifier] = totalMessage; return true; // Buffering } else // Last part { totalMessage.InnerBody = totalMessage.InnerBody; // Refresh... DataRemaining=0 deletes data headers. totalMessage.V2Header.Identifier = newIdentifier - totalMessage.Header.MessageSize; Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, "A splitted message was combined :\r\n" + totalMessage.ToDebugString()); p2pMessage = totalMessage; return false; // We have the whole message } } } // Invalid packet received!!! Ignore and delete it... incompletedP2PV2Messages.Remove(p2pMessage.V2Header.Identifier); Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, "INVALID P2PV2 PACKET received!!! Ignored and deleted:\r\n" + p2pMessage.ToDebugString()); } } } } else // P2PV1 pooling { if ((p2pMessage.V1Header.MessageSize == p2pMessage.V1Header.TotalSize) || // Whole data ((p2pMessage.V1Header.Flags & P2PFlag.Data) == P2PFlag.Data)) // Data message { return false; // No need to buffer } lock (incompletedP2PV1Messages) { if (false == incompletedP2PV1Messages.ContainsKey(p2pMessage.Header.Identifier)) { byte[] totalPayload = new byte[p2pMessage.V1Header.TotalSize]; Array.Copy(p2pMessage.InnerBody, 0, totalPayload, (long)p2pMessage.V1Header.Offset, (long)p2pMessage.V1Header.MessageSize); P2PMessage copyMessage = new P2PMessage(p2pMessage); copyMessage.InnerBody = totalPayload; copyMessage.V1Header.Offset = p2pMessage.V1Header.Offset + p2pMessage.V1Header.MessageSize; incompletedP2PV1Messages[p2pMessage.Header.Identifier] = copyMessage; return true; // Buffering } P2PMessage totalMessage = incompletedP2PV1Messages[p2pMessage.Header.Identifier]; if (p2pMessage.V1Header.TotalSize == totalMessage.V1Header.TotalSize && (p2pMessage.V1Header.Offset + p2pMessage.V1Header.MessageSize) <= totalMessage.Header.TotalSize) { Array.Copy(p2pMessage.InnerBody, 0, totalMessage.InnerBody, (long)p2pMessage.V1Header.Offset, (long)p2pMessage.V1Header.MessageSize); totalMessage.V1Header.Offset = p2pMessage.V1Header.Offset + p2pMessage.V1Header.MessageSize; // Last packet if (totalMessage.V1Header.Offset == p2pMessage.V1Header.TotalSize) { totalMessage.V1Header.Offset = 0; incompletedP2PV1Messages.Remove(p2pMessage.Header.Identifier); p2pMessage = totalMessage; return false; // We have the whole message } return true; // Buffering } // Invalid packet received!!! Ignore and delete it... incompletedP2PV1Messages.Remove(p2pMessage.Header.Identifier); Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, "INVALID P2PV1 PACKET received!!! Ignored and deleted:\r\n" + p2pMessage.ToDebugString()); } } return true; // Invalid packet, don't kill me. }
public P2PSendItem(Contact remote, Guid remoteGuid, P2PMessage message) { Remote = remote; RemoteGuid = remoteGuid; P2PMessage = message; }
public P2PMessageEventArgs(P2PMessage p2pMessage) { this.p2pMessage = p2pMessage; }
internal bool HandleACK(P2PMessage p2pMessage) { bool isAckOrNak = false; if (p2pMessage.Header.IsAcknowledgement || p2pMessage.Header.IsNegativeAck) { P2PAckMessageEventArgs e = null; uint ackNakId = 0; isAckOrNak = true; if (p2pMessage.Version == P2PVersion.P2PV1) { lock (ackHandlersV1) { if (ackHandlersV1.ContainsKey(p2pMessage.Header.AckIdentifier)) { ackNakId = p2pMessage.Header.AckIdentifier; e = ackHandlersV1[ackNakId]; ackHandlersV1.Remove(ackNakId); } } } else if (p2pMessage.Version == P2PVersion.P2PV2) { lock (ackHandlersV2) { if (ackHandlersV2.ContainsKey(p2pMessage.Header.AckIdentifier)) { ackNakId = p2pMessage.Header.AckIdentifier; e = ackHandlersV2[ackNakId]; ackHandlersV2.Remove(ackNakId); } else if (ackHandlersV2.ContainsKey(p2pMessage.V2Header.NakIdentifier)) { ackNakId = p2pMessage.V2Header.NakIdentifier; e = ackHandlersV2[ackNakId]; ackHandlersV2.Remove(ackNakId); } } } if (ackNakId == 0) { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("!!!!!! No AckHandler registered for ack/nak {0}:\r\n{1}", p2pMessage.Header.AckIdentifier, p2pMessage.ToDebugString()), GetType().Name); } else { if (e != null && e.AckHandler != null) { e.AckHandler(p2pMessage); } else { Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("!!!!!! No AckHandler pair for ack {0}\r\n{1}", ackNakId, p2pMessage.ToDebugString()), GetType().Name); } } } return isAckOrNak; }
public override void Start() { base.Start(); if (Sending) { ushort packNum = ++base.P2PSession.Bridge.PackageNo; // Data prep P2PDataMessage prepData = new P2PDataMessage(P2PVersion); prepData.WritePreparationBytes(); if (P2PVersion == P2PVersion.P2PV2) { prepData.V2Header.TFCombination = TFCombination.First; } SendMessage(prepData); Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose, "Data prep sent. Sending whole data...", GetType().Name); // All chunks byte[] allData = new byte[msnObject.Size]; lock (objStream) { using (Stream s = objStream) { s.Position = 0; s.Read(allData, 0, allData.Length); } } P2PDataMessage msg = new P2PDataMessage(P2PVersion); if (P2PVersion == P2PVersion.P2PV1) { msg.V1Header.Flags = P2PFlag.Data; msg.V1Header.AckSessionId = (uint)new Random().Next(50, int.MaxValue); } else if (P2PVersion == P2PVersion.P2PV2) { msg.V2Header.TFCombination = TFCombination.MsnObject | TFCombination.First; msg.V2Header.PackageNumber = packNum; } msg.InnerBody = allData; if (P2PVersion == P2PVersion.P2PV1) { // Object transfer must be finish in MaxTimeout seconds. (p2pv1) // Because ack is received after transfer finished. SendMessage(msg, P2PBridge.MaxTimeout, delegate(P2PMessage ack) { OnTransferFinished(EventArgs.Empty); // Close after remote client sends BYE. }); } else { SendMessage(msg, 0, null); // Register the ACKHandler P2PMessage rak = new P2PMessage(P2PVersion); SendMessage(rak, P2PBridge.DefaultTimeout, delegate(P2PMessage ack) { OnTransferFinished(EventArgs.Empty); // Close after remote client sends BYE. }); } } else { objStream = new MemoryStream(); } }
/// <summary> /// Creates an acknowledgement message to this message. /// </summary> /// <returns></returns> public virtual P2PMessage CreateAcknowledgement() { P2PMessage ack = new P2PMessage(Version); ack.Header = Header.CreateAck(); if (Version == P2PVersion.P2PV1) { ack.Footer = Footer; //Keep the same as the message to acknowladge. } return ack; }
public virtual void SendMessage(P2PMessage msg, P2PMessageSessionEventArgs se) { // if it is a regular message convert it P2PDCMessage p2pMessage = msg as P2PDCMessage; if (p2pMessage == null) { p2pMessage = new P2PDCMessage(msg); } // prepare the message p2pMessage.PrepareMessage(); // this is very bloated! Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose, "Outgoing message:\r\n" + p2pMessage.ToDebugString(), GetType().Name); if (dcSocket != null) Processor.SendSocketData(dcSocket, p2pMessage.GetBytes(), se); else Processor.Send(p2pMessage.GetBytes(), se); }
private void OnSDGDataMessageReceived(MultiMimeMessage multiMimeMessage, Contact sender, Contact by, RoutingInfo routingInfo) { Guid senderEPID = routingInfo.SenderEndPointID; P2PVersion p2pVer = senderEPID == Guid.Empty ? P2PVersion.P2PV1 : P2PVersion.P2PV2; string[] offsets = multiMimeMessage.ContentHeaders.ContainsKey(MIMEContentHeaders.BridgingOffsets) ? multiMimeMessage.ContentHeaders[MIMEContentHeaders.BridgingOffsets].ToString().Split(',') : new string[] { "0" }; List<long> offsetList = new List<long>(); foreach (string os in offsets) { offsetList.Add(long.Parse(os)); } P2PMessage p2pData = new P2PMessage(p2pVer); P2PMessage[] p2pDatas = p2pData.CreateFromOffsets(offsetList.ToArray(), multiMimeMessage.InnerBody); if (multiMimeMessage.ContentHeaders.ContainsKey(MIMEContentHeaders.Pipe)) { SDGBridge.PackageNo = ushort.Parse(multiMimeMessage.ContentHeaders[MIMEContentHeaders.Pipe]); } foreach (P2PMessage m in p2pDatas) { SDGBridge.ProcessP2PMessage(sender, senderEPID, m); } }
public P2PMessage[] CreateFromOffsets(long[] offsets, byte[] allData) { List<P2PMessage> messages = new List<P2PMessage>(offsets.Length); long offset = allData.Length; for (int i = offsets.Length - 1; i >= 0; i--) { P2PMessage m = new P2PMessage(Version); long length = offset - offsets[i]; byte[] part = new byte[length]; Array.Copy(allData, offsets[i], part, 0, length); m.ParseBytes(part); offset -= length; messages.Add(m); } messages.Reverse(); return messages.ToArray(); }
public bool ProcessP2PMessage(P2PBridge bridge, Contact source, Guid sourceGuid, P2PMessage p2pMessage) { // 1) SLP BUFFERING: Combine splitted SLP messages if (slpMessagePool.BufferMessage(ref p2pMessage)) { // * Buffering: Not completed yet, we must wait next packets -OR- // * Invalid packet received: Don't kill me, just ignore it... return(true); } Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose, String.Format("Received P2PMessage from {0}\r\n{1}", bridge.ToString(), p2pMessage.ToDebugString()), GetType().Name); // 2) CHECK SLP: Check destination, source, endpoints SLPMessage slp = p2pMessage.IsSLPData ? p2pMessage.InnerMessage as SLPMessage : null; if (slp != null) { if (!slpHandler.CheckSLPMessage(bridge, source, sourceGuid, p2pMessage, slp)) { return(true); // HANDLED, This SLP is not for us. } } // 3) FIRST SLP MESSAGE: Create applications/sessions based on invitation if (slp != null && slp is SLPRequestMessage && (slp as SLPRequestMessage).Method == "INVITE" && slp.ContentType == "application/x-msnmsgr-sessionreqbody") { uint appId = slp.BodyValues.ContainsKey("AppID") ? uint.Parse(slp.BodyValues["AppID"].Value) : 0; Guid eufGuid = slp.BodyValues.ContainsKey("EUF-GUID") ? new Guid(slp.BodyValues["EUF-GUID"].Value) : Guid.Empty; P2PVersion ver = slp.P2PVersion; if (P2PApplication.IsRegistered(eufGuid, appId)) { P2PSession newSession = FindSessionByCallId(slp.CallId, ver); if (newSession == null) { newSession = new P2PSession(slp as SLPRequestMessage, p2pMessage, nsMessageHandler, bridge); newSession.Closed += P2PSessionClosed; if (newSession.Version == P2PVersion.P2PV2) { p2pV2Sessions.Add(newSession); } else { p2pV1Sessions.Add(newSession); } } else { // P2PSession exists, bridge changed... if (newSession.Bridge != bridge) { // BRIDGETODO } } return(true); } // Not registered application. Decline it without create a new session... slpHandler.SendSLPStatus(bridge, p2pMessage, source, sourceGuid, 603, "Decline"); return(true); } // 4) FIND SESSION: Search session by SessionId/ExpectedIdentifier P2PSession session = FindSession(p2pMessage, slp); if (session != null) { // ResetTimeoutTimer(); // Keep track of theremoteIdentifier // Keep track of the remote identifier session.remoteIdentifier = (p2pMessage.Version == P2PVersion.P2PV2) ? p2pMessage.Header.Identifier + p2pMessage.Header.MessageSize : p2pMessage.Header.Identifier; // Session SLP if (slp != null && slpHandler.HandleP2PSessionSignal(bridge, p2pMessage, slp, session)) { return(true); } // Session Data if (slp == null && session.ProcessP2PData(bridge, p2pMessage)) { return(true); } } return(false); }
internal bool HandleP2PSessionSignal(P2PBridge bridge, P2PMessage p2pMessage, SLPMessage slp, P2PSession session) { if (slp is SLPRequestMessage) { SLPRequestMessage slpRequest = slp as SLPRequestMessage; if (slpRequest.ContentType == "application/x-msnmsgr-sessionclosebody" && slpRequest.Method == "BYE") { if (p2pMessage.Version == P2PVersion.P2PV1) { P2PMessage byeAck = p2pMessage.CreateAcknowledgement(); byeAck.V1Header.Flags = P2PFlag.CloseSession; session.Send(byeAck); } else if (p2pMessage.Version == P2PVersion.P2PV2) { SLPRequestMessage slpMessage = new SLPRequestMessage(session.RemoteContactEPIDString, "BYE"); slpMessage.Target = session.RemoteContactEPIDString; slpMessage.Source = session.LocalContactEPIDString; slpMessage.Branch = session.Invitation.Branch; slpMessage.CallId = session.Invitation.CallId; slpMessage.ContentType = "application/x-msnmsgr-sessionclosebody"; slpMessage.BodyValues["SessionID"] = session.SessionId.ToString(System.Globalization.CultureInfo.InvariantCulture); session.Send(session.WrapSLPMessage(slpMessage)); } session.OnClosed(new ContactEventArgs(session.Remote)); return true; } else { if (slpRequest.ContentType == "application/x-msnmsgr-transreqbody" || slpRequest.ContentType == "application/x-msnmsgr-transrespbody" || slpRequest.ContentType == "application/x-msnmsgr-transdestaddrupdate") { P2PSession.ProcessDirectInvite(slpRequest, nsMessageHandler, session); // Direct connection invite return true; } } } else if (slp is SLPStatusMessage) { SLPStatusMessage slpStatus = slp as SLPStatusMessage; if (slpStatus.Code == 200) // OK { if (slpStatus.ContentType == "application/x-msnmsgr-transrespbody") { P2PSession.ProcessDirectInvite(slpStatus, nsMessageHandler, session); return true; } else { if (session.Application != null) { session.OnActive(EventArgs.Empty); session.Application.Start(); return true; } } } else if (slpStatus.Code == 603) // Decline { session.OnClosed(new ContactEventArgs(session.Remote)); return true; } else if (slpStatus.Code == 500) // Internal Error { return true; } } Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, String.Format("Unhandled SLP Message in session:---->>\r\n{0}", p2pMessage.ToString()), GetType().Name); session.OnError(EventArgs.Empty); return true; }
/// <summary> /// Split big P2PMessages to transport over sb or dc. /// </summary> /// <param name="maxSize"></param> /// <returns></returns> public P2PMessage[] SplitMessage(int maxSize) { uint payloadMessageSize = 0; if (Version == P2PVersion.P2PV1) { payloadMessageSize = V1Header.MessageSize; } if (Version == P2PVersion.P2PV2) { payloadMessageSize = (uint)V2Header.MessageSize - (uint)V2Header.DataPacketHeaderLength; } if (payloadMessageSize <= maxSize) return new P2PMessage[] { this }; List<P2PMessage> chunks = new List<P2PMessage>(); byte[] totalMessage = (InnerBody != null) ? InnerBody : InnerMessage.GetBytes(); long offset = 0; if (Version == P2PVersion.P2PV1) { while (offset < totalMessage.LongLength) { P2PMessage chunkMessage = new P2PMessage(Version); uint messageSize = (uint)Math.Min((uint)maxSize, (totalMessage.LongLength - offset)); byte[] chunk = new byte[messageSize]; Buffer.BlockCopy(totalMessage, (int)offset, chunk, 0, (int)messageSize); chunkMessage.V1Header.Flags = V1Header.Flags; chunkMessage.V1Header.AckIdentifier = V1Header.AckIdentifier; chunkMessage.V1Header.AckTotalSize = V1Header.AckTotalSize; chunkMessage.V1Header.Identifier = V1Header.Identifier; chunkMessage.V1Header.SessionId = V1Header.SessionId; chunkMessage.V1Header.TotalSize = V1Header.TotalSize; chunkMessage.V1Header.Offset = (ulong)offset; chunkMessage.V1Header.MessageSize = messageSize; chunkMessage.InnerBody = chunk; chunkMessage.V1Header.AckSessionId = V1Header.AckSessionId; chunkMessage.Footer = Footer; chunkMessage.PrepareMessage(); chunks.Add(chunkMessage); offset += messageSize; } } if (Version == P2PVersion.P2PV2) { uint nextId = Header.Identifier; long dataRemain = (long)V2Header.DataRemaining; while (offset < totalMessage.LongLength) { P2PMessage chunkMessage = new P2PMessage(Version); int maxDataSize = maxSize; if (offset == 0 && V2Header.HeaderTLVs.Count > 0) { foreach (KeyValuePair<byte, byte[]> keyvalue in V2Header.HeaderTLVs) { chunkMessage.V2Header.HeaderTLVs[keyvalue.Key] = keyvalue.Value; } maxDataSize = maxSize - chunkMessage.V2Header.HeaderLength; } uint dataSize = (uint)Math.Min((uint)maxDataSize, (totalMessage.LongLength - offset)); byte[] chunk = new byte[dataSize]; Buffer.BlockCopy(totalMessage, (int)offset, chunk, 0, (int)dataSize); if (offset == 0) { chunkMessage.V2Header.OperationCode = V2Header.OperationCode; } chunkMessage.V2Header.SessionId = V2Header.SessionId; chunkMessage.V2Header.TFCombination = V2Header.TFCombination; chunkMessage.V2Header.PackageNumber = V2Header.PackageNumber; if (totalMessage.LongLength + dataRemain - (dataSize + offset) > 0) { chunkMessage.V2Header.DataRemaining = (ulong)(totalMessage.LongLength + dataRemain - (dataSize + offset)); } if ((offset != 0) && TFCombination.First == (V2Header.TFCombination & TFCombination.First)) { chunkMessage.V2Header.TFCombination = (TFCombination)(V2Header.TFCombination - TFCombination.First); } chunkMessage.InnerBody = chunk; chunkMessage.Header.Identifier = nextId; nextId += chunkMessage.Header.MessageSize; chunks.Add(chunkMessage); offset += dataSize; } } return chunks.ToArray(); }