public void ProcessPubrec(Int32 packetID)
        {
            _timers.Remove(packetID);
            if (_listener != null)
            {
                _listener.MessageReceived(MessageType.PUBREC);
            }
            MQMessage message = new Pubrel(packetID);

            _timers.Store(message);
            _client.Send(message);
        }
Esempio n. 2
0
        /// <exception cref="MalformedMessageException">Exception is thrown in case of invalid packet format</exception>
        public static MQMessage Decode(IByteBuffer buf)
        {
            MQMessage header = null;

            byte fixedHeader = buf.ReadByte();

            LengthDetails length = DecodeLength(buf);

            MessageType type = (MessageType)((fixedHeader >> 4) & 0xf);

            switch (type)
            {
            case MessageType.CONNECT:

                Byte[] nameValue = new byte[buf.ReadUnsignedShort()];
                buf.ReadBytes(nameValue, 0, nameValue.Length);
                String name = Encoding.UTF8.GetString(nameValue);
                if (!name.Equals(Connect.PROTOCOL_NAME))
                {
                    throw new MalformedMessageException("CONNECT, protocol-name set to " + name);
                }

                Byte protocolLevel = buf.ReadByte();
                Byte contentFlags  = buf.ReadByte();

                Boolean userNameFlag = (((contentFlags >> 7) & 1) == 1) ? true : false;
                Boolean userPassFlag = (((contentFlags >> 6) & 1) == 1) ? true : false;
                Boolean willRetain   = (((contentFlags >> 5) & 1) == 1) ? true : false;

                Int32 QOSValue = ((contentFlags & 0x1f) >> 3) & 3;
                QOS   willQos  = (QOS)QOSValue;
                if (!Enum.IsDefined(typeof(QOS), QOSValue))
                {
                    throw new MalformedMessageException("CONNECT, will QoS set to " + willQos);
                }
                Boolean willFlag = (((contentFlags >> 2) & 1) == 1) ? true : false;

                if (willQos != QOS.AT_MOST_ONCE && !willFlag)
                {
                    throw new MalformedMessageException("CONNECT, will QoS set to " + willQos + ", willFlag not set");
                }

                if (willRetain && !willFlag)
                {
                    throw new MalformedMessageException("CONNECT, will retain set, willFlag not set");
                }

                Boolean cleanSession = (((contentFlags >> 1) & 1) == 1) ? true : false;

                Boolean reservedFlag = ((contentFlags & 1) == 1) ? true : false;
                if (reservedFlag)
                {
                    throw new MalformedMessageException("CONNECT, reserved flag set to true");
                }

                int keepalive = buf.ReadUnsignedShort();

                Byte[] clientIdValue = new byte[buf.ReadUnsignedShort()];
                buf.ReadBytes(clientIdValue, 0, clientIdValue.Length);
                String clientID = Encoding.UTF8.GetString(clientIdValue);
                if (!StringVerifier.verify(clientID))
                {
                    throw new MalformedMessageException("ClientID contains restricted characters: U+0000, U+D000-U+DFFF");
                }

                String willTopic   = null;
                Byte[] willMessage = null;
                String username    = null;
                String password    = null;

                Will will = null;
                if (willFlag)
                {
                    if (buf.ReadableBytes < 2)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    byte[] willTopicValue = new byte[buf.ReadUnsignedShort()];
                    if (buf.ReadableBytes < willTopicValue.Length)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    buf.ReadBytes(willTopicValue, 0, willTopicValue.Length);

                    willTopic = Encoding.UTF8.GetString(willTopicValue);
                    if (!StringVerifier.verify(willTopic))
                    {
                        throw new MalformedMessageException("WillTopic contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                    }

                    if (buf.ReadableBytes < 2)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    willMessage = new byte[buf.ReadUnsignedShort()];
                    if (buf.ReadableBytes < willMessage.Length)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    buf.ReadBytes(willMessage, 0, willMessage.Length);
                    if (willTopic.Length == 0)
                    {
                        throw new MalformedMessageException("invalid will encoding");
                    }
                    will = new Will(new Topic(willTopic, willQos), willMessage, willRetain);
                    if (!will.IsValid())
                    {
                        throw new MalformedMessageException("invalid will encoding");
                    }
                }

                if (userNameFlag)
                {
                    if (buf.ReadableBytes < 2)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    byte[] userNameValue = new byte[buf.ReadUnsignedShort()];
                    if (buf.ReadableBytes < userNameValue.Length)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    buf.ReadBytes(userNameValue, 0, userNameValue.Length);
                    username = Encoding.UTF8.GetString(userNameValue);
                    if (!StringVerifier.verify(username))
                    {
                        throw new MalformedMessageException("Username contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                    }
                }

                if (userPassFlag)
                {
                    if (buf.ReadableBytes < 2)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    byte[] userPassValue = new byte[buf.ReadUnsignedShort()];
                    if (buf.ReadableBytes < userPassValue.Length)
                    {
                        throw new MalformedMessageException("Invalid encoding will/username/password");
                    }

                    buf.ReadBytes(userPassValue, 0, userPassValue.Length);
                    password = Encoding.UTF8.GetString(userPassValue);
                    if (!StringVerifier.verify(password))
                    {
                        throw new MalformedMessageException("Password contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                    }
                }

                if (buf.ReadableBytes > 0)
                {
                    throw new MalformedMessageException("Invalid encoding will/username/password");
                }

                Connect connect = new Connect(username, password, clientID, cleanSession, keepalive, will);
                if (protocolLevel != 4)
                {
                    connect.ProtocolLevel = protocolLevel;
                }
                header = connect;
                break;

            case MessageType.CONNACK:
                byte sessionPresentValue = buf.ReadByte();
                if (sessionPresentValue != 0 && sessionPresentValue != 1)
                {
                    throw new MalformedMessageException("CONNACK, session-present set to " + (sessionPresentValue & 0xff));
                }

                Boolean isPresent = sessionPresentValue == 1 ? true : false;

                Int32       connackByte = ((Int32)buf.ReadByte()) & 0xFF;
                ConnackCode connackCode = (ConnackCode)connackByte;
                if (!Enum.IsDefined(typeof(ConnackCode), connackByte))
                {
                    throw new MalformedMessageException("Invalid connack code: " + connackByte);
                }
                header = new Connack(isPresent, connackCode);
                break;

            case MessageType.PUBLISH:
                int dataLength = length.Length;
                fixedHeader &= 0xf;

                Boolean dup = (((fixedHeader >> 3) & 1) == 1) ? true : false;

                QOSValue = (fixedHeader & 0x07) >> 1;
                QOS qos = (QOS)QOSValue;
                if (!Enum.IsDefined(typeof(QOS), (byte)QOSValue))
                {
                    throw new MalformedMessageException("invalid QoS value");
                }
                if (dup && qos == QOS.AT_MOST_ONCE)
                {
                    throw new MalformedMessageException("PUBLISH, QoS-0 dup flag present");
                }

                Boolean retain = ((fixedHeader & 1) == 1) ? true : false;

                byte[] topicNameValue = new byte[buf.ReadUnsignedShort()];
                buf.ReadBytes(topicNameValue, 0, topicNameValue.Length);
                String topicName = Encoding.UTF8.GetString(topicNameValue);
                if (!StringVerifier.verify(topicName))
                {
                    throw new MalformedMessageException("Publish-topic contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                }
                dataLength -= topicName.Length + 2;

                Int32?packetID = null;
                if (qos != QOS.AT_MOST_ONCE)
                {
                    packetID = buf.ReadUnsignedShort();
                    if (packetID < 0 || packetID > 65535)
                    {
                        throw new MalformedMessageException("Invalid PUBLISH packetID encoding");
                    }
                    dataLength -= 2;
                }
                byte[] data = new byte[dataLength];
                if (dataLength > 0)
                {
                    buf.ReadBytes(data, 0, data.Length);
                }
                header = new Publish(packetID, new Topic(topicName, qos), data, retain, dup);
                break;

            case MessageType.PUBACK:
                header = new Puback(buf.ReadUnsignedShort());
                break;

            case MessageType.PUBREC:
                header = new Pubrec(buf.ReadUnsignedShort());
                break;

            case MessageType.PUBREL:
                header = new Pubrel(buf.ReadUnsignedShort());
                break;

            case MessageType.PUBCOMP:
                header = new Pubcomp(buf.ReadUnsignedShort());
                break;

            case MessageType.SUBSCRIBE:
                Int32        subID         = buf.ReadUnsignedShort();
                List <Topic> subscriptions = new List <Topic>();
                while (buf.IsReadable())
                {
                    byte[] value = new byte[buf.ReadUnsignedShort()];
                    buf.ReadBytes(value, 0, value.Length);
                    QOSValue = buf.ReadByte();
                    QOS requestedQos = (QOS)QOSValue;
                    if (!Enum.IsDefined(typeof(QOS), QOSValue))
                    {
                        throw new MalformedMessageException("Subscribe qos must be in range from 0 to 2: " + requestedQos);
                    }
                    String topic = Encoding.UTF8.GetString(value);
                    if (!StringVerifier.verify(topic))
                    {
                        throw new MalformedMessageException("Subscribe topic contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                    }
                    Topic subscription = new Topic(topic, requestedQos);
                    subscriptions.Add(subscription);
                }
                if (subscriptions.Count == 0)
                {
                    throw new MalformedMessageException("Subscribe with 0 topics");
                }

                header = new Subscribe(subID, subscriptions.ToArray());
                break;

            case MessageType.SUBACK:
                Int32             subackID    = buf.ReadUnsignedShort();
                List <SubackCode> subackCodes = new List <SubackCode>();
                while (buf.IsReadable())
                {
                    Int32      subackByte = ((Int32)buf.ReadByte()) & 0xFF;
                    SubackCode subackCode = (SubackCode)subackByte;
                    if (!Enum.IsDefined(typeof(SubackCode), subackByte))
                    {
                        throw new MalformedMessageException("Invalid suback code: " + subackByte);
                    }
                    subackCodes.Add(subackCode);
                }
                if (subackCodes.Count == 0)
                {
                    throw new MalformedMessageException("Suback with 0 return-codes");
                }

                header = new Suback(subackID, subackCodes);
                break;

            case MessageType.UNSUBSCRIBE:
                Int32         unsubID           = buf.ReadUnsignedShort();
                List <String> unsubscribeTopics = new List <String>();
                while (buf.IsReadable())
                {
                    byte[] value = new byte[buf.ReadUnsignedShort()];
                    buf.ReadBytes(value, 0, value.Length);
                    String topic = Encoding.UTF8.GetString(value);
                    if (!StringVerifier.verify(topic))
                    {
                        throw new MalformedMessageException("Unsubscribe topic contains one or more restricted characters: U+0000, U+D000-U+DFFF");
                    }
                    unsubscribeTopics.Add(topic);
                }
                if (unsubscribeTopics.Count == 0)
                {
                    throw new MalformedMessageException("Unsubscribe with 0 topics");
                }
                header = new Unsubscribe(unsubID, unsubscribeTopics.ToArray());
                break;

            case MessageType.UNSUBACK:
                header = new Unsuback(buf.ReadUnsignedShort());
                break;

            case MessageType.PINGREQ:
                header = new Pingreq();
                break;

            case MessageType.PINGRESP:
                header = new Pingresp();
                break;

            case MessageType.DISCONNECT:
                header = new Disconnect();
                break;

            default:
                throw new MalformedMessageException("Invalid header type: " + type);
            }

            if (buf.IsReadable())
            {
                throw new MalformedMessageException("unexpected bytes in content");
            }

            if (length.Length != header.GetLength())
            {
                throw new MalformedMessageException("Invalid length. Encoded: " + length.Length + ", actual: " + header.GetLength());
            }

            return(header);
        }
Esempio n. 3
0
        /// <exception cref="MalformedMessageException">Exception is thrown in case of invalid packet format</exception>
        public static IByteBuffer encode(MQMessage header)
        {
            int         length = header.GetLength();
            IByteBuffer buf    = GetBuffer(length);
            MessageType type   = header.MessageType;

            switch (type)
            {
            case MessageType.CONNECT:
                Connect connect = (Connect)header;
                if (connect.Will != null && !connect.Will.IsValid())
                {
                    throw new MalformedMessageException("invalid will encoding");
                }

                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(Connect.DEFAULT_PROTOCOL_LEVEL);
                buf.WriteBytes(Encoding.UTF8.GetBytes(connect.Name));
                buf.WriteByte(connect.ProtocolLevel);
                byte contentFlags = 0;
                if (connect.CleanSession)
                {
                    contentFlags += 0x02;
                }

                if (connect.Will != null)
                {
                    contentFlags += 0x04;
                    contentFlags += (byte)(((Int32)connect.Will.Topic.Qos) << 3);
                    if (connect.Will.Retain)
                    {
                        contentFlags += 0x20;
                    }
                }

                if (connect.Username != null)
                {
                    contentFlags += 0x40;
                }

                if (connect.Password != null)
                {
                    contentFlags += 0x80;
                }

                buf.WriteByte(contentFlags);
                buf.WriteShort(connect.Keepalive);
                buf.WriteShort(connect.ClientID.Length);
                buf.WriteBytes(Encoding.UTF8.GetBytes(connect.ClientID));

                if (connect.Will != null)
                {
                    String willTopic = connect.Will.Topic.Name;
                    if (willTopic != null)
                    {
                        buf.WriteShort(willTopic.Length);
                        buf.WriteBytes(Encoding.UTF8.GetBytes(willTopic));
                    }

                    byte[] willMessage = connect.Will.Content;
                    if (willMessage != null)
                    {
                        buf.WriteShort(willMessage.Length);
                        buf.WriteBytes(willMessage);
                    }
                }

                String username = connect.Username;
                if (username != null)
                {
                    buf.WriteShort(username.Length);
                    buf.WriteBytes(Encoding.UTF8.GetBytes(username));
                }

                String password = connect.Password;
                if (password != null)
                {
                    buf.WriteShort(password.Length);
                    buf.WriteBytes(Encoding.UTF8.GetBytes(password));
                }
                break;

            case MessageType.CONNACK:
                Connack connack = (Connack)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteBoolean(connack.SessionPresent);
                buf.WriteByte((Int32)connack.ReturnCode);
                break;

            case MessageType.PUBLISH:
                Publish publish   = (Publish)header;
                byte    firstByte = (byte)(((Int32)type) << 4);
                if (publish.Dup)
                {
                    firstByte += 0x08;
                }

                firstByte += (byte)(((Int32)publish.Topic.Qos) << 1);

                if (publish.Retain)
                {
                    firstByte += 0x01;
                }

                buf.SetByte(0, firstByte);
                buf.WriteShort(publish.Topic.Name.Length);
                buf.WriteBytes(Encoding.UTF8.GetBytes(publish.Topic.Name));
                if (publish.PacketID.HasValue)
                {
                    buf.WriteShort(publish.PacketID.Value);
                }
                buf.WriteBytes(publish.Content);
                break;

            case MessageType.PUBACK:
                Puback puback = (Puback)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(puback.PacketID.Value);
                break;

            case MessageType.PUBREC:
                Pubrec pubrec = (Pubrec)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(pubrec.PacketID.Value);
                break;

            case MessageType.PUBREL:
                Pubrel pubrel = (Pubrel)header;
                buf.SetByte(0, (byte)((((Int32)type) << 4) | 0x2));
                buf.WriteShort(pubrel.PacketID.Value);
                break;

            case MessageType.PUBCOMP:
                Pubcomp pubcomp = (Pubcomp)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(pubcomp.PacketID.Value);
                break;

            case MessageType.SUBSCRIBE:
                Subscribe sub = (Subscribe)header;
                buf.SetByte(0, (byte)((((Int32)type) << 4) | 0x2));
                buf.WriteShort(sub.PacketID.Value);
                foreach (Topic subscription in sub.Topics)
                {
                    buf.WriteShort(subscription.Name.Length);
                    buf.WriteBytes(Encoding.UTF8.GetBytes(subscription.Name));
                    buf.WriteByte((Int32)subscription.Qos);
                }
                break;

            case MessageType.SUBACK:
                Suback suback = (Suback)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(suback.PacketID.Value);
                foreach (SubackCode code in suback.ReturnCodes)
                {
                    buf.WriteByte((Int32)code);
                }
                break;

            case MessageType.UNSUBSCRIBE:
                Unsubscribe unsub = (Unsubscribe)header;
                buf.SetByte(0, (byte)((((Int32)type) << 4) | 0x2));
                buf.WriteShort(unsub.PacketID.Value);
                foreach (String topic in unsub.Topics)
                {
                    buf.WriteShort(topic.Length);
                    buf.WriteBytes(Encoding.UTF8.GetBytes(topic));
                }
                break;

            case MessageType.UNSUBACK:
                Unsuback unsuback = (Unsuback)header;
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                buf.WriteShort(unsuback.PacketID.Value);
                break;

            case MessageType.DISCONNECT:
            case MessageType.PINGREQ:
            case MessageType.PINGRESP:
                buf.SetByte(0, (byte)(((Int32)type) << 4));
                break;

            default:
                throw new MalformedMessageException("Invalid header type: " + type);
            }

            return(buf);
        }