mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-24 11:56:48 +00:00
MINOR: sample: Add converts to parses MQTT messages
This patch implements a couple of converters to validate and extract data from a MQTT (Message Queuing Telemetry Transport) message. The validation consists of a few checks as well as "packet size" validation. The extraction can get any field from the variable header and the payload. This is limited to CONNECT and CONNACK packet types only. All other messages are considered as invalid. It is not a problem for now because only the first packet on each side can be parsed (CONNECT for the client and CONNACK for the server). MQTT 3.1.1 and 5.0 are supported. Reviewed and Fixed by Christopher Faulet <cfaulet@haproxy.com>
This commit is contained in:
parent
7983b8687e
commit
e279ca6bbe
2
Makefile
2
Makefile
@ -843,7 +843,7 @@ OBJS += src/mux_h2.o src/mux_fcgi.o src/http_ana.o src/stream.o \
|
||||
src/ebimtree.o src/uri_auth.o src/freq_ctr.o src/ebsttree.o \
|
||||
src/ebistree.o src/auth.o src/wdt.o src/http_acl.o \
|
||||
src/hpack-enc.o src/hpack-huff.o src/ebtree.o src/base64.o \
|
||||
src/hash.o src/dgram.o src/version.o src/fix.o
|
||||
src/hash.o src/dgram.o src/version.o src/fix.o src/mqtt.o
|
||||
|
||||
ifneq ($(TRACE),)
|
||||
OBJS += src/calltrace.o
|
||||
|
@ -15410,6 +15410,93 @@ mod(<value>)
|
||||
This prefix is followed by a name. The separator is a '.'. The name may only
|
||||
contain characters 'a-z', 'A-Z', '0-9', '.' and '_'.
|
||||
|
||||
mqtt_field_value(<packettype>,<fieldname or property ID>)
|
||||
Returns value of <fieldname> found in input MQTT payload of type
|
||||
<packettype>.
|
||||
<packettype> can be either a string (case insensitive matching) or a numeric
|
||||
value corresponding to the type of packet we're supposed to extract data
|
||||
from.
|
||||
Supported string and integers can be found here:
|
||||
https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718021
|
||||
https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901022
|
||||
|
||||
<fieldname> depends on <packettype> and can be any of the following below.
|
||||
(note that <fieldname> matching is case insensitive).
|
||||
<property id> can only be found in MQTT v5.0 streams. check this table:
|
||||
https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901029
|
||||
|
||||
- CONNECT (or 1): flags, protocol_name, protocol_version, client_identifier,
|
||||
will_topic, will_payload, username, password, keepalive
|
||||
OR any property ID as a numeric value (for MQTT v5.0
|
||||
packets only):
|
||||
17: Session Expiry Interval
|
||||
33: Receive Maximum
|
||||
39: Maximum Packet Size
|
||||
34: Topic Alias Maximum
|
||||
25: Request Response Information
|
||||
23: Request Problem Information
|
||||
21: Authentication Method
|
||||
22: Authentication Data
|
||||
18: Will Delay Interval
|
||||
1: Payload Format Indicator
|
||||
2: Message Expiry Interval
|
||||
3: Content Type
|
||||
8: Response Topic
|
||||
9: Correlation Data
|
||||
Not supported yet:
|
||||
38: User Property
|
||||
|
||||
- CONNACK (or 2): flags, protocol_version, reason_code
|
||||
OR any property ID as a numeric value (for MQTT v5.0
|
||||
packets only):
|
||||
17: Session Expiry Interval
|
||||
33: Receive Maximum
|
||||
36: Maximum QoS
|
||||
37: Retain Available
|
||||
39: Maximum Packet Size
|
||||
18: Assigned Client Identifier
|
||||
34: Topic Alias Maximum
|
||||
31: Reason String
|
||||
40; Wildcard Subscription Available
|
||||
41: Subscription Identifiers Available
|
||||
42: Shared Subscription Available
|
||||
19: Server Keep Alive
|
||||
26: Response Information
|
||||
28: Server Reference
|
||||
21: Authentication Method
|
||||
22: Authentication Data
|
||||
Not supported yet:
|
||||
38: User Property
|
||||
|
||||
Due to current HAProxy design, only the first message sent by the client and
|
||||
the server can be parsed. Thus this converter can extract data only from
|
||||
CONNECT and CONNACK packet types. CONNECT is the first message sent by the
|
||||
client and CONNACK is the first response sent by the server.
|
||||
|
||||
Example:
|
||||
|
||||
acl data_in_buffer req.len ge 4
|
||||
tcp-request content set-var(txn.username) \
|
||||
req.payload(0,0),mqtt_field_value(connect,protocol_name) \
|
||||
if data_in_buffer
|
||||
# do the same as above
|
||||
tcp-request content set-var(txn.username) \
|
||||
req.payload(0,0),mqtt_field_value(1,protocol_name) \
|
||||
if data_in_buffer
|
||||
|
||||
mqtt_is_valid
|
||||
Checks that the binary input is a valid MQTT packet. It returns a boolean.
|
||||
|
||||
Due to current HAProxy design, only the first message sent by the client and
|
||||
the server can be parsed. Thus this converter can extract data only from
|
||||
CONNECT and CONNACK packet types. CONNECT is the first message sent by the
|
||||
client and CONNACK is the first response sent by the server.
|
||||
|
||||
Example:
|
||||
|
||||
acl data_in_buffer req.len ge 4
|
||||
tcp-request content reject unless req.payload(0,0),mqtt_is_valid
|
||||
|
||||
mul(<value>)
|
||||
Multiplies the input value of type signed integer by <value>, and returns
|
||||
the product as an signed integer. In case of overflow, the largest possible
|
||||
|
309
include/haproxy/mqtt-t.h
Normal file
309
include/haproxy/mqtt-t.h
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* include/haproxy/mqtt.h
|
||||
* This file contains structure declarations for MQTT protocol.
|
||||
*
|
||||
* Copyright 2020 Baptiste Assmann <bedis9@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, version 2.1
|
||||
* exclusively.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _HAPROXY_MQTT_T_H
|
||||
#define _HAPROXY_MQTT_T_H
|
||||
|
||||
#include <import/ist.h>
|
||||
|
||||
/* MQTT protocol version
|
||||
* In MQTT 3.1.1, version is called "level"
|
||||
*/
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
#define MQTT_VERSION_5_0 5
|
||||
|
||||
/*
|
||||
* return code when parsing / validating MQTT messages
|
||||
*/
|
||||
#define MQTT_INVALID_MESSAGE -1
|
||||
#define MQTT_NEED_MORE_DATA 0
|
||||
#define MQTT_VALID_MESSAGE 1
|
||||
|
||||
|
||||
/*
|
||||
* MQTT Control Packet Type: MQTT_CPT_*
|
||||
*
|
||||
* Part of the fixed headers, encoded on the first packet byte :
|
||||
*
|
||||
* +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+
|
||||
* | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+
|
||||
* | field | MQTT Control Packet Type | Flags specific to each Control Packet type |
|
||||
* +-------+---------------------------------------------+--------------------------------------------+
|
||||
*
|
||||
* Don't forget to "left offset by 4 bits (<< 4)" the values below when matching against the fixed
|
||||
* header collected in a MQTT packet.
|
||||
*
|
||||
* value 0x0 is reserved and forbidden
|
||||
*/
|
||||
enum {
|
||||
MQTT_CPT_INVALID = 0,
|
||||
|
||||
MQTT_CPT_CONNECT,
|
||||
MQTT_CPT_CONNACK,
|
||||
MQTT_CPT_PUBLISH,
|
||||
MQTT_CPT_PUBACK,
|
||||
MQTT_CPT_PUBREC,
|
||||
MQTT_CPT_PUBREL,
|
||||
MQTT_CPT_PUBCOMP,
|
||||
MQTT_CPT_SUBSCRIBE,
|
||||
MQTT_CPT_SUBACK,
|
||||
MQTT_CPT_UNSUBSCRIBE,
|
||||
MQTT_CPT_UNSUBACK,
|
||||
MQTT_CPT_PINGREQ,
|
||||
MQTT_CPT_PINGRESP,
|
||||
MQTT_CPT_DISCONNECT,
|
||||
MQTT_CPT_AUTH,
|
||||
MQTT_CPT_ENTRIES /* used to mark the end/size of our MQTT_CPT_* list */
|
||||
};
|
||||
|
||||
/* MQTT CONNECT packet flags */
|
||||
#define MQTT_CONNECT_FL_RESERVED 0x01
|
||||
#define MQTT_CONNECT_FL_CLEAN_SESSION 0x02
|
||||
#define MQTT_CONNECT_FL_WILL 0x04
|
||||
#define MQTT_CONNECT_FL_WILL_QOS 0x18 /* covers 2 bits 00011000 */
|
||||
#define MQTT_CONNECT_FL_WILL_RETAIN 0x20
|
||||
#define MQTT_CONNECT_FL_PASSWORD 0x40
|
||||
#define MQTT_CONNECT_FL_USERNAME 0x80
|
||||
|
||||
/* MQTT packet properties indentifiers
|
||||
* https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901029
|
||||
*/
|
||||
#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01
|
||||
#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02
|
||||
#define MQTT_PROP_CONTENT_TYPE 0x03
|
||||
#define MQTT_PROP_RESPONSE_TOPIC 0x08
|
||||
#define MQTT_PROP_CORRELATION_DATA 0x09
|
||||
#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11
|
||||
#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12
|
||||
#define MQTT_PROP_SERVER_KEEPALIVE 0x13
|
||||
#define MQTT_PROP_AUTHENTICATION_METHOD 0x15
|
||||
#define MQTT_PROP_AUTHENTICATION_DATA 0x16
|
||||
#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17
|
||||
#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18
|
||||
#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19
|
||||
#define MQTT_PROP_RESPONSE_INFORMATION 0x1A
|
||||
#define MQTT_PROP_SERVER_REFERENCE 0x1C
|
||||
#define MQTT_PROP_RECEIVE_MAXIMUM 0x21
|
||||
#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22
|
||||
#define MQTT_PROP_MAXIMUM_QOS 0x24
|
||||
#define MQTT_PROP_RETAIN_AVAILABLE 0x25
|
||||
#define MQTT_PROP_USER_PROPERTIES 0x26
|
||||
#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27
|
||||
#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28
|
||||
#define MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE 0x29
|
||||
#define MQTT_PROP_SHARED_SUBSRIPTION_AVAILABLE 0x2A
|
||||
#define MQTT_PROP_REASON_STRING 0x1F
|
||||
#define MQTT_PROP_LAST 0xFF
|
||||
|
||||
/* MQTT minimal packet size */
|
||||
#define MQTT_MIN_PKT_SIZE 2
|
||||
#define MQTT_REMAINING_LENGHT_MAX_SIZE 4
|
||||
|
||||
/* list of supported capturable Field Names and configuration file string */
|
||||
enum {
|
||||
MQTT_FN_INVALID = 0,
|
||||
|
||||
MQTT_FN_FLAGS,
|
||||
MQTT_FN_REASON_CODE,
|
||||
MQTT_FN_PROTOCOL_NAME,
|
||||
MQTT_FN_PROTOCOL_VERSION,
|
||||
MQTT_FN_CLIENT_IDENTIFIER,
|
||||
MQTT_FN_WILL_TOPIC,
|
||||
MQTT_FN_WILL_PAYLOAD,
|
||||
MQTT_FN_USERNAME,
|
||||
MQTT_FN_PASSWORD,
|
||||
MQTT_FN_KEEPALIVE,
|
||||
|
||||
/* MQTT 5.0 properties
|
||||
* https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901029
|
||||
*/
|
||||
MQTT_FN_PAYLOAD_FORMAT_INDICATOR,
|
||||
MQTT_FN_MESSAGE_EXPIRY_INTERVAL,
|
||||
MQTT_FN_CONTENT_TYPE,
|
||||
MQTT_FN_RESPONSE_TOPIC,
|
||||
MQTT_FN_CORRELATION_DATA,
|
||||
MQTT_FN_SUBSCRIPTION_IDENTIFIER,
|
||||
MQTT_FN_SESSION_EXPIRY_INTERVAL,
|
||||
MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER,
|
||||
MQTT_FN_SERVER_KEEPALIVE,
|
||||
MQTT_FN_AUTHENTICATION_METHOD,
|
||||
MQTT_FN_AUTHENTICATION_DATA,
|
||||
MQTT_FN_REQUEST_PROBLEM_INFORMATION,
|
||||
MQTT_FN_DELAY_INTERVAL,
|
||||
MQTT_FN_REQUEST_RESPONSE_INFORMATION,
|
||||
MQTT_FN_RESPONSE_INFORMATION,
|
||||
MQTT_FN_SERVER_REFERENCE,
|
||||
MQTT_FN_REASON_STRING,
|
||||
MQTT_FN_RECEIVE_MAXIMUM,
|
||||
MQTT_FN_TOPIC_ALIAS_MAXIMUM,
|
||||
MQTT_FN_TOPIC_ALIAS,
|
||||
MQTT_FN_MAXIMUM_QOS,
|
||||
MQTT_FN_RETAIN_AVAILABLE,
|
||||
MQTT_FN_USER_PROPERTY,
|
||||
MQTT_FN_MAXIMUM_PACKET_SIZE,
|
||||
MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE,
|
||||
MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE,
|
||||
MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE,
|
||||
|
||||
MQTT_FN_ENTRIES /* this one must always be the latest one */
|
||||
};
|
||||
|
||||
/* MQTT field string bit, for easy match using bitmasks
|
||||
* ATTENTION: "user-properties" are not supported for now
|
||||
*/
|
||||
enum {
|
||||
MQTT_FN_BIT_FLAGS = (1ULL << MQTT_FN_FLAGS),
|
||||
MQTT_FN_BIT_REASON_CODE = (1ULL << MQTT_FN_REASON_CODE),
|
||||
MQTT_FN_BIT_PROTOCOL_NAME = (1ULL << MQTT_FN_PROTOCOL_NAME),
|
||||
MQTT_FN_BIT_PROTOCOL_VERSION = (1ULL << MQTT_FN_PROTOCOL_VERSION),
|
||||
MQTT_FN_BIT_CLIENT_IDENTIFIER = (1ULL << MQTT_FN_CLIENT_IDENTIFIER),
|
||||
MQTT_FN_BIT_WILL_TOPIC = (1ULL << MQTT_FN_WILL_TOPIC),
|
||||
MQTT_FN_BIT_WILL_PAYLOAD = (1ULL << MQTT_FN_WILL_PAYLOAD),
|
||||
MQTT_FN_BIT_USERNAME = (1ULL << MQTT_FN_USERNAME),
|
||||
MQTT_FN_BIT_PASSWORD = (1ULL << MQTT_FN_PASSWORD),
|
||||
MQTT_FN_BIT_KEEPALIVE = (1ULL << MQTT_FN_KEEPALIVE),
|
||||
MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR = (1ULL << MQTT_FN_PAYLOAD_FORMAT_INDICATOR),
|
||||
MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL = (1ULL << MQTT_FN_MESSAGE_EXPIRY_INTERVAL),
|
||||
MQTT_FN_BIT_CONTENT_TYPE = (1ULL << MQTT_FN_CONTENT_TYPE),
|
||||
MQTT_FN_BIT_RESPONSE_TOPIC = (1ULL << MQTT_FN_RESPONSE_TOPIC),
|
||||
MQTT_FN_BIT_CORRELATION_DATA = (1ULL << MQTT_FN_CORRELATION_DATA),
|
||||
MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER = (1ULL << MQTT_FN_SUBSCRIPTION_IDENTIFIER),
|
||||
MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL = (1ULL << MQTT_FN_SESSION_EXPIRY_INTERVAL),
|
||||
MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER = (1ULL << MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER),
|
||||
MQTT_FN_BIT_SERVER_KEEPALIVE = (1ULL << MQTT_FN_SERVER_KEEPALIVE),
|
||||
MQTT_FN_BIT_AUTHENTICATION_METHOD = (1ULL << MQTT_FN_AUTHENTICATION_METHOD),
|
||||
MQTT_FN_BIT_AUTHENTICATION_DATA = (1ULL << MQTT_FN_AUTHENTICATION_DATA),
|
||||
MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION = (1ULL << MQTT_FN_REQUEST_PROBLEM_INFORMATION),
|
||||
MQTT_FN_BIT_DELAY_INTERVAL = (1ULL << MQTT_FN_DELAY_INTERVAL),
|
||||
MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION = (1ULL << MQTT_FN_REQUEST_RESPONSE_INFORMATION),
|
||||
MQTT_FN_BIT_RESPONSE_INFORMATION = (1ULL << MQTT_FN_RESPONSE_INFORMATION),
|
||||
MQTT_FN_BIT_SERVER_REFERENCE = (1ULL << MQTT_FN_SERVER_REFERENCE),
|
||||
MQTT_FN_BIT_REASON_STRING = (1ULL << MQTT_FN_REASON_STRING),
|
||||
MQTT_FN_BIT_RECEIVE_MAXIMUM = (1ULL << MQTT_FN_RECEIVE_MAXIMUM),
|
||||
MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM = (1ULL << MQTT_FN_TOPIC_ALIAS_MAXIMUM),
|
||||
MQTT_FN_BIT_TOPIC_ALIAS = (1ULL << MQTT_FN_TOPIC_ALIAS),
|
||||
MQTT_FN_BIT_MAXIMUM_QOS = (1ULL << MQTT_FN_MAXIMUM_QOS),
|
||||
MQTT_FN_BIT_RETAIN_AVAILABLE = (1ULL << MQTT_FN_RETAIN_AVAILABLE),
|
||||
MQTT_FN_BIT_USER_PROPERTY = (1ULL << MQTT_FN_USER_PROPERTY),
|
||||
MQTT_FN_BIT_MAXIMUM_PACKET_SIZE = (1ULL << MQTT_FN_MAXIMUM_PACKET_SIZE),
|
||||
MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE = (1ULL << MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE),
|
||||
MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE= (1ULL << MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE),
|
||||
MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE = (1ULL << MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE),
|
||||
};
|
||||
|
||||
/* structure to host fields for a MQTT CONNECT packet */
|
||||
#define MQTT_PROP_USER_PROPERTY_ENTRIES 5
|
||||
struct connect {
|
||||
struct {
|
||||
struct ist protocol_name;
|
||||
uint8_t protocol_version;
|
||||
uint8_t flags;
|
||||
uint16_t keepalive;
|
||||
|
||||
struct {
|
||||
uint32_t session_expiry_interval;
|
||||
uint16_t receive_maximum;
|
||||
uint32_t maximum_packet_size;
|
||||
uint16_t topic_alias_maximum;
|
||||
uint8_t request_response_information;
|
||||
uint8_t request_problem_information;
|
||||
struct {
|
||||
struct ist name;
|
||||
struct ist value;
|
||||
} user_props[MQTT_PROP_USER_PROPERTY_ENTRIES];
|
||||
struct ist authentication_method;
|
||||
struct ist authentication_data;
|
||||
} props;
|
||||
} var_hdr;
|
||||
struct {
|
||||
struct ist client_identifier;
|
||||
struct {
|
||||
uint32_t delay_interval;
|
||||
uint8_t payload_format_indicator;
|
||||
uint32_t message_expiry_interval;
|
||||
struct ist content_type;
|
||||
struct ist response_topic;
|
||||
struct ist correlation_data;
|
||||
struct {
|
||||
struct ist name;
|
||||
struct ist value;
|
||||
} user_props[MQTT_PROP_USER_PROPERTY_ENTRIES];
|
||||
} will_props;
|
||||
struct ist will_topic;
|
||||
struct ist will_payload;
|
||||
struct ist username;
|
||||
struct ist password;
|
||||
} payload;
|
||||
};
|
||||
|
||||
/* structure to host fields for a MQTT CONNACK packet */
|
||||
struct connack {
|
||||
struct {
|
||||
uint8_t protocol_version;
|
||||
uint8_t flags;
|
||||
uint8_t reason_code;
|
||||
struct {
|
||||
uint32_t session_expiry_interval;
|
||||
uint16_t receive_maximum;
|
||||
uint8_t maximum_qos;
|
||||
uint8_t retain_available;
|
||||
uint32_t maximum_packet_size;
|
||||
struct ist assigned_client_identifier;
|
||||
uint16_t topic_alias_maximum;
|
||||
struct ist reason_string;
|
||||
struct {
|
||||
struct ist name;
|
||||
struct ist value;
|
||||
} user_props[MQTT_PROP_USER_PROPERTY_ENTRIES];
|
||||
uint8_t wildcard_subscription_available;
|
||||
uint8_t subscription_identifiers_available;
|
||||
uint8_t shared_subsription_available;
|
||||
uint16_t server_keepalive;
|
||||
struct ist response_information;
|
||||
struct ist server_reference;
|
||||
struct ist authentication_method;
|
||||
struct ist authentication_data;
|
||||
} props;
|
||||
} var_hdr;
|
||||
};
|
||||
|
||||
/* structure to host a MQTT packet */
|
||||
struct mqtt_pkt {
|
||||
struct {
|
||||
uint8_t type; /* MQTT_CPT_* */
|
||||
uint8_t flags; /* MQTT_CPT_FL* */
|
||||
uint32_t remaining_length;
|
||||
} fixed_hdr;
|
||||
union {
|
||||
struct connect connect;
|
||||
struct connack connack;
|
||||
} data;
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_MQTT_T_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
118
include/haproxy/mqtt.h
Normal file
118
include/haproxy/mqtt.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* include/haproxt/mqtt.h
|
||||
* This file contains structure declarations for MQTT protocol.
|
||||
*
|
||||
* Copyright 2020 Baptiste Assmann <bedis9@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, version 2.1
|
||||
* exclusively.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _HAPROXY_MQTT_H
|
||||
#define _HAPROXY_MQTT_H
|
||||
|
||||
#include <import/ist.h>
|
||||
|
||||
#include <haproxy/mqtt-t.h>
|
||||
#include <haproxy/tools.h>
|
||||
|
||||
/* expected flags for control packets */
|
||||
extern uint8_t mqtt_cpt_flags[MQTT_CPT_ENTRIES];
|
||||
|
||||
/* MQTT field string names */
|
||||
extern const struct ist mqtt_fields_string[MQTT_FN_ENTRIES];
|
||||
|
||||
/* list of supported capturable field names for each MQTT control packet type */
|
||||
extern const uint64_t mqtt_fields_per_packet[MQTT_CPT_ENTRIES];
|
||||
|
||||
int mqtt_validate_message(const struct ist msg, struct mqtt_pkt *mpkt);
|
||||
struct ist mqtt_field_value(const struct ist msg, int type, int fieldname_id);
|
||||
|
||||
/*
|
||||
* Return a MQTT packet type ID based found in <str>.
|
||||
* <str> can be a number or a string and returned value will always be the numeric value.
|
||||
*
|
||||
* If <str> can't be translated into an ID, then MQTT_CPT_INVALID (0) is returned.
|
||||
*/
|
||||
static inline int mqtt_typeid(struct ist str)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = strl2ui(str.ptr, istlen(str));
|
||||
if ((id >= MQTT_CPT_CONNECT) && (id < MQTT_CPT_ENTRIES))
|
||||
return id;
|
||||
|
||||
else if (isteqi(str, ist("CONNECT")) != 0)
|
||||
return MQTT_CPT_CONNECT;
|
||||
else if (isteqi(str, ist("CONNACK")) != 0)
|
||||
return MQTT_CPT_CONNACK;
|
||||
else if (isteqi(str, ist("PUBLISH")) != 0)
|
||||
return MQTT_CPT_PUBLISH;
|
||||
else if (isteqi(str, ist("PUBACK")) != 0)
|
||||
return MQTT_CPT_PUBACK;
|
||||
else if (isteqi(str, ist("PUBREC")) != 0)
|
||||
return MQTT_CPT_PUBREC;
|
||||
else if (isteqi(str, ist("PUBREL")) != 0)
|
||||
return MQTT_CPT_PUBREL;
|
||||
else if (isteqi(str, ist("PUBCOMP")) != 0)
|
||||
return MQTT_CPT_PUBCOMP;
|
||||
else if (isteqi(str, ist("SUBSCRIBE")) != 0)
|
||||
return MQTT_CPT_SUBSCRIBE;
|
||||
else if (isteqi(str, ist("SUBACK")) != 0)
|
||||
return MQTT_CPT_SUBACK;
|
||||
else if (isteqi(str, ist("UNSUBSCRIBE")) != 0)
|
||||
return MQTT_CPT_UNSUBSCRIBE;
|
||||
else if (isteqi(str, ist("UNSUBACK")) != 0)
|
||||
return MQTT_CPT_UNSUBACK;
|
||||
else if (isteqi(str, ist("PINGREQ")) != 0)
|
||||
return MQTT_CPT_PINGREQ;
|
||||
else if (isteqi(str, ist("PINGRESP")) != 0)
|
||||
return MQTT_CPT_PINGRESP;
|
||||
else if (isteqi(str, ist("DISCONNECT")) != 0)
|
||||
return MQTT_CPT_DISCONNECT;
|
||||
else if (isteqi(str, ist("AUTH")) != 0)
|
||||
return MQTT_CPT_AUTH;
|
||||
|
||||
return MQTT_CPT_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* validate that <str> is a field that can be extracted from a <type> MQTT packet
|
||||
*
|
||||
* return the field name ID (MQTT_FN_*) if a match is found, MQTT_FN_INVALID (0) otherwise.
|
||||
*/
|
||||
static inline int mqtt_check_type_fieldname(int type, struct ist str)
|
||||
{
|
||||
int i, id = MQTT_FN_INVALID;
|
||||
|
||||
for (i = 0; i < MQTT_FN_ENTRIES; i++) {
|
||||
if (isteqi(str, mqtt_fields_string[i])) {
|
||||
if (mqtt_fields_per_packet[type] & (1ULL << i))
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_MQTT_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
1280
src/mqtt.c
Normal file
1280
src/mqtt.c
Normal file
File diff suppressed because it is too large
Load Diff
104
src/sample.c
104
src/sample.c
@ -31,6 +31,7 @@
|
||||
#include <haproxy/hash.h>
|
||||
#include <haproxy/http.h>
|
||||
#include <haproxy/istbuf.h>
|
||||
#include <haproxy/mqtt.h>
|
||||
#include <haproxy/net_helper.h>
|
||||
#include <haproxy/protobuf.h>
|
||||
#include <haproxy/proxy.h>
|
||||
@ -3296,6 +3297,105 @@ static int sample_conv_fix_is_valid(const struct arg *arg_p, struct sample *smp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the field value of an input binary sample containing an MQTT packet.
|
||||
* Takes 2 mandatory arguments:
|
||||
* - packet type
|
||||
* - field name
|
||||
*
|
||||
* return 1 if the field was found, 0 if not.
|
||||
*/
|
||||
static int sample_conv_mqtt_field_value(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
struct ist pkt, value;
|
||||
int type, fieldname_id;
|
||||
|
||||
pkt = ist2(smp->data.u.str.area, smp->data.u.str.data);
|
||||
type = arg_p[0].data.sint;
|
||||
fieldname_id = arg_p[1].data.sint;
|
||||
|
||||
smp->flags &= ~SMP_F_MAY_CHANGE;
|
||||
value = mqtt_field_value(pkt, type, fieldname_id);
|
||||
if (!istlen(value)) {
|
||||
if (isttest(value)) {
|
||||
/* value != IST_NULL, need more data */
|
||||
smp->flags |= SMP_F_MAY_CHANGE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
smp->data.u.str = ist2buf(value);
|
||||
smp->flags |= SMP_F_CONST;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function checks the "mqtt_field_value" converter configuration.
|
||||
* It expects a known packet type name or ID and a field name, in this order
|
||||
*
|
||||
* Args[0] will be turned into a MQTT_CPT_* value for direct maching when parsing
|
||||
* a packet.
|
||||
*/
|
||||
static int sample_conv_mqtt_field_value_check(struct arg *args, struct sample_conv *conv,
|
||||
const char *file, int line, char **err)
|
||||
{
|
||||
int type, fieldname_id;
|
||||
|
||||
/* check the MQTT packet type is valid */
|
||||
type = mqtt_typeid(ist2(args[0].data.str.area, args[0].data.str.data));
|
||||
if (type == MQTT_CPT_INVALID) {
|
||||
memprintf(err, "Unknown MQTT type '%s'", args[0].data.str.area);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check the field name belongs to the MQTT packet type */
|
||||
fieldname_id = mqtt_check_type_fieldname(type, ist2(args[1].data.str.area, args[1].data.str.data));
|
||||
if (fieldname_id == MQTT_FN_INVALID) {
|
||||
memprintf(err, "Unknown MQTT field name '%s' for packet type '%s'", args[1].data.str.area,
|
||||
args[0].data.str.area);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* save numeric counterparts of type and field name */
|
||||
chunk_destroy(&args[0].data.str);
|
||||
chunk_destroy(&args[1].data.str);
|
||||
args[0].type = ARGT_SINT;
|
||||
args[0].data.sint = type;
|
||||
args[1].type = ARGT_SINT;
|
||||
args[1].data.sint = fieldname_id;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that <smp> contains a valid MQTT message
|
||||
*
|
||||
* The function returns 1 if the check was run to its end, 0 otherwise.
|
||||
* The result of the analyse itself is stored in <smp> as a boolean.
|
||||
*/
|
||||
static int sample_conv_mqtt_is_valid(const struct arg *arg_p, struct sample *smp, void *private)
|
||||
{
|
||||
struct ist msg;
|
||||
|
||||
msg = ist2(smp->data.u.str.area, smp->data.u.str.data);
|
||||
|
||||
smp->flags &= ~SMP_F_MAY_CHANGE;
|
||||
switch (mqtt_validate_message(msg, NULL)) {
|
||||
case FIX_VALID_MESSAGE:
|
||||
smp->data.type = SMP_T_BOOL;
|
||||
smp->data.u.sint = 1;
|
||||
return 1;
|
||||
case FIX_NEED_MORE_DATA:
|
||||
smp->flags |= SMP_F_MAY_CHANGE;
|
||||
return 0;
|
||||
case FIX_INVALID_MESSAGE:
|
||||
smp->data.type = SMP_T_BOOL;
|
||||
smp->data.u.sint = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function checks the "strcmp" converter's arguments and extracts the
|
||||
* variable name and its scope.
|
||||
*/
|
||||
@ -3888,6 +3988,10 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||
{ "fix_is_valid", sample_conv_fix_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL },
|
||||
{ "fix_tag_value", sample_conv_fix_tag_value, ARG1(1,STR), sample_conv_fix_value_check, SMP_T_BIN, SMP_T_BIN },
|
||||
|
||||
/* MQTT converters */
|
||||
{ "mqtt_is_valid", sample_conv_mqtt_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL },
|
||||
{ "mqtt_field_value", sample_conv_mqtt_field_value, ARG2(2,STR,STR), sample_conv_mqtt_field_value_check, SMP_T_BIN, SMP_T_STR },
|
||||
|
||||
{ "iif", sample_conv_iif, ARG2(2, STR, STR), NULL, SMP_T_BOOL, SMP_T_STR },
|
||||
|
||||
{ "and", sample_conv_binary_and, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
|
||||
|
Loading…
Reference in New Issue
Block a user