Example #1
0
        public static void Write(StunRecord record, Stream stream)
        {
            var padLen = record.MessageIntegrity is null ? 8 : 32;

            var bytes = new byte[StunConstants.RecordHeaderLength + record.MessageLength + padLen];

            var idx = 0;

            BinaryPrimitives.WriteInt16BigEndian(bytes.AsSpan(idx), (short)record.MessageType);
            idx += 2;
            BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), (ushort)(record.MessageLength + padLen /*size of MessageIntegrity and Fingerprint*/));
            idx += 2;
            BinaryPrimitives.WriteInt32BigEndian(bytes.AsSpan(idx), StunRecord.MessageCookie);
            idx += 4;
            record.MessageTransactionId.CopyTo(bytes.AsSpan(idx));
            idx += record.MessageTransactionId.Length;

            foreach (var attr in record.StunAttributes)
            {
                BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), (ushort)attr.Type);
                idx += 2;
                BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), attr.Length);
                idx += 2;
                if (attr.Value != null)
                {
                    attr.Value.CopyTo(bytes.AsSpan(idx));
                    idx += attr.Value.Length;
                }
                idx += attr.Padding;
            }

            var miRec = record.MessageIntegrity;

            if (miRec != null)
            {
                BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), (ushort)miRec.Type);
                idx += 2;
                BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), miRec.Length);
                idx += 2;
                miRec.Value.CopyTo(bytes.AsSpan(idx));
                idx += miRec.Value.Length;
            }

            var fiRec = record.Fingerprint;

            BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), (ushort)fiRec.Type);
            idx += 2;
            BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(idx), fiRec.Length);
            idx += 2;
            fiRec.Value.CopyTo(bytes.AsSpan(idx));

            stream.Write(bytes);
        }
Example #2
0
        public static StunRecord Read(byte[] bytes)
        {
            var record = new StunRecord();

            var idx = 0;

            record.MessageType = (StunMessageType)BinaryPrimitives.ReadUInt16BigEndian(bytes.AsSpan(idx));
            idx += 2;
            var stunMessageLength = BinaryPrimitives.ReadUInt16BigEndian(bytes.AsSpan(idx));

            idx += 2;
            var stunMessageCookie = BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(idx));

            idx += 4;

            if (stunMessageCookie != StunRecord.MessageCookie)
            {
                throw new InvalidOperationException("Malformed STUN packet");
            }

            record.MessageTransactionId = bytes.AsSpan(idx, 12).ToArray();
            idx += 12;
            record.StunAttributes = new List <StunAttribute>();

            while (idx < bytes.Length)
            {
                var stunAttributeType = (StunAttributeType)BinaryPrimitives.ReadUInt16BigEndian(bytes.AsSpan(idx));
                idx += 2;
                var stunAttributeLength = BinaryPrimitives.ReadUInt16BigEndian(bytes.AsSpan(idx));
                idx += 2;
                var stunAttributePaddingRemainder = (byte)(stunAttributeLength % 4);
                var stunAttributePadding          = (byte)(stunAttributePaddingRemainder == 0 ? 0 : 4 - stunAttributePaddingRemainder);
                var stunAttributeValue            = bytes.AsSpan(idx, stunAttributeLength).ToArray();
                idx += stunAttributeLength;

                if (stunAttributePadding > 0)
                {
                    idx += stunAttributePadding;
                }

                StunAttribute stunAttribute;

                switch (stunAttributeType)
                {
                case StunAttributeType.Username:
                    stunAttribute = new UsernameAttribute();
                    break;

                case StunAttributeType.IceControlled:
                    stunAttribute = new IceControlledAttribute();
                    break;

                case StunAttributeType.Priority:
                    stunAttribute = new PriorityAttribute();
                    break;

                case StunAttributeType.MessageIntegrity:
                    stunAttribute = new MessageIntegrityAttribute();
                    break;

                case StunAttributeType.Fingerprint:
                    stunAttribute = new FingerprintAttribute();
                    break;

                default:
                    stunAttribute = new StunAttribute
                    {
                        Type = stunAttributeType
                    };
                    break;
                }

                stunAttribute.Value = stunAttributeValue;

                if (stunAttribute.Type != StunAttributeType.MessageIntegrity &&
                    stunAttribute.Type != StunAttributeType.Fingerprint)
                {
                    record.StunAttributes.Add(stunAttribute);
                }
            }

            return(record);
        }