using STUN.Message.Attributes; using STUN.Message.Enums; using STUN.Utils; using System; using System.Collections.Generic; using System.Linq; namespace STUN.Message { /// /// https://tools.ietf.org/html/rfc5389#section-15 /// public class Attribute { /* Length 是大端 必须4字节对齐 对齐的字节可以是任意值 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Value (variable) .... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ public AttributeType Type { get; set; } = AttributeType.Useless; public ushort Length { get; set; } public int RealLength => Type == AttributeType.Useless ? 0 : 4 + Length + (4 - Length % 4) % 4; public IAttribute Value { get; set; } private readonly byte[] _magicCookie; private readonly byte[] _transactionId; public Attribute() { } public Attribute(byte[] magicCookie, byte[] transactionId) { if (magicCookie.Length != 4 || transactionId.Length != 12) { throw new ArgumentException(@"Wrong Transaction ID length"); } _magicCookie = magicCookie; _transactionId = transactionId; } public IEnumerable ToBytes() { var res = new List(); res.AddRange(Convert.ToUInt16(Type).ToBe()); res.AddRange(Length.ToBe()); res.AddRange(Value.Bytes); var n = (4 - res.Count % 4) % 4; // 填充的字节数 res.AddRange(BitUtils.GetRandomBytes(n)); return res; } /// /// Parse 成功字节,0 则表示 Parse 失败 /// public int TryParse(byte[] bytes) { if (bytes.Length < 4) return 0; Type = (AttributeType)BitUtils.FromBe(bytes[0], bytes[1]); Length = BitUtils.FromBe(bytes[2], bytes[3]); if (bytes.Length < 4 + Length) return 0; var value = bytes.Skip(4).Take(Length).ToArray(); IAttribute t = Type switch { AttributeType.MappedAddress => new MappedAddressAttribute(), AttributeType.XorMappedAddress => new XorMappedAddressAttribute(_magicCookie, _transactionId), AttributeType.ResponseAddress => new ResponseAddressAttribute(), AttributeType.ChangeRequest => new ChangeRequestAttribute(), AttributeType.SourceAddress => new SourceAddressAttribute(), AttributeType.ChangedAddress => new ChangedAddressAttribute(), AttributeType.OtherAddress => new OtherAddressAttribute(), AttributeType.ReflectedFrom => new ReflectedFromAttribute(), AttributeType.ErrorCode => new ErrorCodeAttribute(), //TODO:more _ => new UselessAttribute() }; Value = t.TryParse(value) ? t : null; return 4 + Length + (4 - Length % 4) % 4; // 对齐 } } }