/// <summary> /// TODO: Documentation ConnectionBind /// </summary> /// <param name="connectionId"></param> /// <param name="username"></param> /// <param name="password"></param> public void ConnectionBind(StunAttribute connectionId, String username, String password) { this.TurnTcpManager = new TurnManager(this.StunClient.ServerEP, this.StunClient.ProtocolType, this.StunClient.ClientCertificate, this.StunClient.RemoteCertificateValidation); this.TurnTcpManager.OnAllocateSucceed += (object sender, TurnAllocation allocation, StunMessage sentMsg, StunMessage receivedMsg) => { StunMessage msg = new StunMessage(StunMethodType.ConnectionBind, StunMethodClass.Request, StunUtilities.NewTransactionId); msg.Turn.ConnectionId = connectionId; msg.Stun.Username = new UTF8Attribute(StunAttributeType.Username, allocation.Username); msg.Stun.Realm = new UTF8Attribute(StunAttributeType.Realm, allocation.Realm); msg.Stun.Nonce = new UTF8Attribute(StunAttributeType.Nonce, allocation.Nonce); msg.AddMessageIntegrity(allocation.Password, true); this.TurnTcpManager.StunClient.BeginSendMessage(msg, this.TurnTcpManager.StunClient.Socket); }; this.TurnTcpManager.OnConnectionBindSucceed += (object sender, Socket connectedSocket, StunMessage receivedMsg) => { this.TurnTcpManager.Allocations.Clear(); this.TurnTcpManager.StunClient.Cancel = true; if (this.OnConnectionBindSucceed != null) { this.OnConnectionBindSucceed(sender, connectedSocket, receivedMsg); } }; this.TurnTcpManager.Connect(); this.TurnTcpManager.Allocate(username, password); }
/// <summary> /// Parses an array of bytes containing every attributes of a message and /// add them to managed and unmanaged attributes lists /// </summary> /// <param name="attributes">The array of byte which contains every attributes of a message</param> private void ImportAttributes(byte[] attributes) { Int32 offset = 0; Int32 attributesLength = attributes.Length; while (offset < attributesLength) { // We retrieve length and add it attribute header length (4 bytes) UInt16 valueLength = BitConverter.ToUInt16(StunUtilities.SubArray(attributes, offset + 2, 2), 0); UInt16 attributeLength = (UInt16)(StunUtilities.ReverseBytes(valueLength) + 4); // Adjust original length to a padded 32bit length if (attributeLength % 4 != 0) { attributeLength = (UInt16)(attributeLength + (4 - attributeLength % 4)); } StunAttribute attr = StunUtilities.SubArray(attributes, offset, attributeLength); // When doing auto conversion of a byte array, according to STUN RFC 5389 // only the first attribute of a given type must be taken into account this.SetAttribute(attr, false); offset += attributeLength; } }
/// <summary> /// Add an attribute to this message /// If the attribute is not managed by this library, it will be added to the UnmanagedAttributes list /// </summary> /// <param name="attribute">The attribute to add</param> /// <param name="replaceExistingAttribute"> /// If TRUE, any existing attribute of the same type as the attribute parameter /// will be replaced by the attribute in parameter /// </param> public void SetAttribute(StunAttribute attribute, Boolean replaceExistingAttribute) { if (attribute.Type == StunAttributeType.Unmanaged) { this.unmanagedAttributesList.Add(attribute); } else { if (!this.attributesList.ContainsKey(attribute.Type)) { this.attributesList.Add(attribute.Type, attribute); } else if (replaceExistingAttribute) { this.attributesList[attribute.Type] = attribute; } } }
/// <summary> /// Add the MESSAGE-INTEGRITY attribute to the message. /// This should be fired just before a call to StunClient.SendMessage as it needs existing /// attribute to be computed. But these attributes MUST be added before the MESSAGE-ATTRIBUTE /// except for FINGERPRINT attribute which MUST be added at the end of the StunMessage /// and also used in the MESSAGE-INTEGRITY computation /// </summary> /// <param name="password">The password used to authenticate the StunMessage</param> /// <param name="useLongTermCredentials">True if this StunMessage should authenticate using longterm credentials</param> public void AddMessageIntegrity(String password, Boolean useLongTermCredentials) { password = new SASLprep().Prepare(password); byte[] hmacSha1Key; if (useLongTermCredentials) { if (this.Stun.Username == null) { throw new ArgumentException("USERNAME attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Username"); } if (this.Stun.Realm == null) { throw new ArgumentException("REALM attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Realm"); } using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { String valueToHashMD5 = String.Format(CultureInfo.CurrentCulture, "{0}:{1}:{2}", this.Stun.Username.ValueString, this.Stun.Realm.ValueString, password); hmacSha1Key = md5.ComputeHash(StunMessage.Encoder.GetBytes(valueToHashMD5)); } } else { hmacSha1Key = StunMessage.Encoder.GetBytes(password); } StunAttribute messageIntegrity = new StunAttribute(StunAttributeType.MessageIntegrity, this.ComputeHMAC(hmacSha1Key)); this.SetAttribute(messageIntegrity); }
/// <summary> /// TODO: Documentation ConnectionBind /// </summary> /// <param name="connectionId"></param> /// <param name="username"></param> /// <param name="password"></param> public void ConnectionBind(StunAttribute connectionId, String username, String password) { this.TurnTcpManager = new TurnManager(this.StunClient.ServerEP, this.StunClient.ProtocolType, this.StunClient.ClientCertificate, this.StunClient.RemoteCertificateValidation); this.TurnTcpManager.OnAllocateSucceed += (object sender, TurnAllocation allocation, StunMessage sentMsg, StunMessage receivedMsg) => { StunMessage msg = new StunMessage(StunMethodType.ConnectionBind, StunMethodClass.Request, StunUtilities.NewTransactionId); msg.Turn.ConnectionId = connectionId; msg.Stun.Username = new UTF8Attribute(StunAttributeType.Username, allocation.Username); msg.Stun.Realm = new UTF8Attribute(StunAttributeType.Realm, allocation.Realm); msg.Stun.Nonce = new UTF8Attribute(StunAttributeType.Nonce, allocation.Nonce); msg.AddMessageIntegrity(allocation.Password, true); this.TurnTcpManager.StunClient.BeginSendMessage(msg, this.TurnTcpManager.StunClient.Socket); }; this.TurnTcpManager.OnConnectionBindSucceed += (object sender, Socket connectedSocket, StunMessage receivedMsg) => { this.TurnTcpManager.Allocations.Clear(); this.TurnTcpManager.StunClient.Cancel = true; if (this.OnConnectionBindSucceed != null) this.OnConnectionBindSucceed(sender, connectedSocket, receivedMsg); }; this.TurnTcpManager.Connect(); this.TurnTcpManager.Allocate(username, password); }
/// <summary> /// Add an attribute to this message /// Any existing attribute of the same type will be replaced by the attribute in parameter. /// </summary> /// <param name="attribute">The attribute to add</param> public void SetAttribute(StunAttribute attribute) { this.SetAttribute(attribute, true); }
/// <summary> /// Add the MESSAGE-INTEGRITY attribute to the message. /// This should be fired just before a call to StunClient.SendMessage as it needs existing /// attribute to be computed. But these attributes MUST be added before the MESSAGE-ATTRIBUTE /// except for FINGERPRINT attribute which MUST be added at the end of the StunMessage /// and also used in the MESSAGE-INTEGRITY computation /// </summary> /// <param name="password">The password used to authenticate the StunMessage</param> /// <param name="useLongTermCredentials">True if this StunMessage should authenticate using longterm credentials</param> public void AddMessageIntegrity(String password, Boolean useLongTermCredentials) { password = new SASLprep().Prepare(password); byte[] hmacSha1Key; if (useLongTermCredentials) { if (this.Stun.Username == null) throw new ArgumentException("USERNAME attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Username"); if (this.Stun.Realm == null) throw new ArgumentException("REALM attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Realm"); using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { String valueToHashMD5 = String.Format(CultureInfo.CurrentCulture, "{0}:{1}:{2}", this.Stun.Username.ValueString, this.Stun.Realm.ValueString, password); hmacSha1Key = md5.ComputeHash(StunMessage.Encoder.GetBytes(valueToHashMD5)); } } else { hmacSha1Key = StunMessage.Encoder.GetBytes(password); } StunAttribute messageIntegrity = new StunAttribute(StunAttributeType.MessageIntegrity, this.ComputeHMAC(hmacSha1Key)); this.SetAttribute(messageIntegrity); }
/// <summary> /// Add an attribute to this message /// If the attribute is not managed by this library, it will be added to the UnmanagedAttributes list /// </summary> /// <param name="attribute">The attribute to add</param> /// <param name="replaceExistingAttribute"> /// If TRUE, any existing attribute of the same type as the attribute parameter /// will be replaced by the attribute in parameter /// </param> public void SetAttribute(StunAttribute attribute, Boolean replaceExistingAttribute) { if (attribute.Type == StunAttributeType.Unmanaged) { this.unmanagedAttributesList.Add(attribute); } else { if (!this.attributesList.ContainsKey(attribute.Type)) this.attributesList.Add(attribute.Type, attribute); else if (replaceExistingAttribute) this.attributesList[attribute.Type] = attribute; } }
/// <summary> /// Constructs a new StunAttribute /// </summary> /// <param name="type">The type of this StunAttribute</param> /// <param name="value">The value of this StunAttribute</param> public StunAttribute(StunAttributeType type, byte[] value) : this(type, StunAttribute.AttributeTypeToBytes(type), value) { }