File: mqtt5_encoder.h

package info (click to toggle)
aws-crt-python 0.20.4%2Bdfsg-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 72,656 kB
  • sloc: ansic: 381,805; python: 23,008; makefile: 6,251; sh: 4,536; cpp: 699; ruby: 208; java: 77; perl: 73; javascript: 46; xml: 11
file content (357 lines) | stat: -rw-r--r-- 16,190 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/**
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#ifndef AWS_MQTT_MQTT5_ENCODER_H
#define AWS_MQTT_MQTT5_ENCODER_H

#include <aws/mqtt/mqtt.h>

#include <aws/common/array_list.h>
#include <aws/common/byte_buf.h>

#include <aws/mqtt/v5/mqtt5_types.h>

struct aws_mqtt5_client;
struct aws_mqtt5_encoder;
struct aws_mqtt5_outbound_topic_alias_resolver;

/**
 * We encode packets by looking at all of the packet's values/properties and building a sequence of encoding steps.
 * Each encoding step is a simple, primitive operation of which there are two types:
 *   (1) encode an integer in some fashion (fixed width or variable length)
 *   (2) encode a raw sequence of bytes (either a cursor or a stream)
 *
 * Once the encoding step sequence is constructed, we do the actual encoding by iterating the sequence, performing
 * the steps.  This is interruptible/resumable, so we can perform encodings that span multiple buffers easily.
 */
enum aws_mqtt5_encoding_step_type {
    /* encode a single byte */
    AWS_MQTT5_EST_U8,

    /* encode a 16 bit unsigned integer in network order */
    AWS_MQTT5_EST_U16,

    /* encode a 32 bit unsigned integer in network order */
    AWS_MQTT5_EST_U32,

    /*
     * encode a 32 bit unsigned integer using MQTT variable length encoding. It is assumed that the 32 bit value has
     * already been checked against the maximum allowed value for variable length encoding.
     */
    AWS_MQTT5_EST_VLI,

    /*
     * encode an array of bytes as referenced by a cursor.  Most of the time this step is paired with either a prefix
     * specifying the number of bytes or a preceding variable length integer from which the data length can be
     * computed.
     */
    AWS_MQTT5_EST_CURSOR,

    /* encode a stream of bytes.  The same context that applies to cursor encoding above also applies here. */
    AWS_MQTT5_EST_STREAM,
};

/**
 * Elemental unit of packet encoding.
 */
struct aws_mqtt5_encoding_step {
    enum aws_mqtt5_encoding_step_type type;
    union {
        uint8_t value_u8;
        uint16_t value_u16;
        uint32_t value_u32;
        struct aws_byte_cursor value_cursor;
        struct aws_input_stream *value_stream;
    } value;
};

/**
 * signature of a function that can takes a view assumed to be a specific packet type and appends the encoding
 * steps necessary to encode that packet into the encoder
 */
typedef int(aws_mqtt5_encode_begin_packet_type_fn)(struct aws_mqtt5_encoder *encoder, const void *view);

/**
 * Per-packet-type table of encoding functions
 */
struct aws_mqtt5_encoder_function_table {
    aws_mqtt5_encode_begin_packet_type_fn *encoders_by_packet_type[16];
};

/**
 * Configuration options for an mqtt5 encoder.  Everything is optional at this time.
 */
struct aws_mqtt5_encoder_options {
    struct aws_mqtt5_client *client;
    const struct aws_mqtt5_encoder_function_table *encoders;
};

/**
 * An encoder is just a list of steps and a current location for the encoding process within that list.
 */
struct aws_mqtt5_encoder {
    struct aws_mqtt5_encoder_options config;

    struct aws_array_list encoding_steps;
    size_t current_encoding_step_index;

    struct aws_mqtt5_outbound_topic_alias_resolver *topic_alias_resolver;
};

/**
 * Encoding proceeds until either
 *  (1) a fatal error is reached
 *  (2) the steps are done
 *  (3) no room is left in the buffer
 */
enum aws_mqtt5_encoding_result {
    /*
     * A fatal error state was reached during encoding.  This forces a connection shut down with no DISCONNECT.
     * An error can arise from several sources:
     *   (1) Bug in the encoder (length calculations, step calculations)
     *   (2) Bug in the view validation logic that is assumed to have caught any illegal/forbidden situations like
     *       values-too-big, etc...
     *   (3) System error when reading from a stream that is more than just a memory buffer
     *
     *  Regardless of the origin, the connection is in an unusable state once this happens.
     *
     *  If the encode function returns this value, aws last error will have an error value in it
     */
    AWS_MQTT5_ER_ERROR,

    /* All encoding steps in the encoder have been completed.  The encoder is ready for a new packet. */
    AWS_MQTT5_ER_FINISHED,

    /*
     * The buffer has been filled as closely to full as possible and there are still encoding steps remaining that
     * have not been completed.  It is technically possible to hit a permanent out-of-room state if the buffer size
     * is less than 4.  Don't do that.
     */
    AWS_MQTT5_ER_OUT_OF_ROOM,
};

AWS_EXTERN_C_BEGIN

/**
 * Initializes an mqtt5 encoder
 *
 * @param encoder encoder to initialize
 * @param allocator allocator to use for all memory allocation
 * @param options encoder configuration options to use
 * @return
 */
AWS_MQTT_API int aws_mqtt5_encoder_init(
    struct aws_mqtt5_encoder *encoder,
    struct aws_allocator *allocator,
    struct aws_mqtt5_encoder_options *options);

/**
 * Cleans up an mqtt5 encoder
 *
 * @param encoder encoder to free up all resources for
 */
AWS_MQTT_API void aws_mqtt5_encoder_clean_up(struct aws_mqtt5_encoder *encoder);

/**
 * Resets the state on an mqtt5 encoder.  Ok to call after a failure to a packet _begin_packet() function.  Not ok to
 * call after a failed call to aws_mqtt5_encoder_encode_to_buffer()
 *
 * @param encoder encoder to reset
 * @return
 */
AWS_MQTT_API void aws_mqtt5_encoder_reset(struct aws_mqtt5_encoder *encoder);

/**
 * Adds all of the primitive encoding steps necessary to encode an MQTT5 packet
 *
 * @param encoder encoder to add encoding steps to
 * @param packet_type type of packet to encode
 * @param packet_view view into the corresponding packet type
 * @return success/failure
 */
AWS_MQTT_API int aws_mqtt5_encoder_append_packet_encoding(
    struct aws_mqtt5_encoder *encoder,
    enum aws_mqtt5_packet_type packet_type,
    const void *packet_view);

/*
 * We intend that the client implementation only submits one packet at a time to the encoder, corresponding to the
 * current operation of the client.  This is an important property to maintain to allow us to correlate socket
 * completions with packets/operations sent.  It's the client's responsibility though; the encoder is dumb.
 *
 * The client will greedily use as much of an iomsg's buffer as it can if there are multiple operations (packets)
 * queued and there is sufficient room.
 */

/**
 * Asks the encoder to encode as much as it possibly can into the supplied buffer.
 *
 * @param encoder encoder to do the encoding
 * @param buffer where to encode into
 * @return result of the encoding process.  aws last error will be set appropriately.
 */
AWS_MQTT_API enum aws_mqtt5_encoding_result aws_mqtt5_encoder_encode_to_buffer(
    struct aws_mqtt5_encoder *encoder,
    struct aws_byte_buf *buffer);

/**
 * Sets the outbound alias resolver that the encoder should use during the lifetime of a connection
 *
 * @param encoder encoder to apply outbound topic alias resolution to
 * @param resolver outbound topic alias resolver
 */
AWS_MQTT_API void aws_mqtt5_encoder_set_outbound_topic_alias_resolver(
    struct aws_mqtt5_encoder *encoder,
    struct aws_mqtt5_outbound_topic_alias_resolver *resolver);

/**
 * Default encoder table.  Tests copy it and augment with additional functions in order to do round-trip encode-decode
 * tests for packets that are only encoded on the server.
 */
AWS_MQTT_API extern const struct aws_mqtt5_encoder_function_table *g_aws_mqtt5_encoder_default_function_table;

AWS_EXTERN_C_END

/******************************************************************************************************************
 *  Encoding helper functions and macros - placed in header so that test-only encoding has access
 ******************************************************************************************************************/

AWS_EXTERN_C_BEGIN

/**
 * Utility function to calculate the encoded packet size of a given packet view.  Used to validate operations
 * against the server's maximum packet size.
 *
 * @param packet_type type of packet the view represents
 * @param packet_view packet view
 * @param packet_size output parameter, set if the size was successfully calculated
 * @return success/failure
 */
AWS_MQTT_API int aws_mqtt5_packet_view_get_encoded_size(
    enum aws_mqtt5_packet_type packet_type,
    const void *packet_view,
    size_t *packet_size);

/**
 * Encodes a variable length integer to a buffer.  Assumes the buffer has been checked for sufficient room (this
 * is not a streaming/resumable operation)
 *
 * @param buf buffer to encode to
 * @param value value to encode
 * @return success/failure
 */
AWS_MQTT_API int aws_mqtt5_encode_variable_length_integer(struct aws_byte_buf *buf, uint32_t value);

/**
 * Computes how many bytes are necessary to encode a value as a variable length integer
 * @param value value to encode
 * @param encode_size output parameter for the encoding size
 * @return success/failure where failure is exclusively value-is-illegal-and-too-large-to-encode
 */
AWS_MQTT_API int aws_mqtt5_get_variable_length_encode_size(size_t value, size_t *encode_size);

AWS_MQTT_API void aws_mqtt5_encoder_push_step_u8(struct aws_mqtt5_encoder *encoder, uint8_t value);

AWS_MQTT_API void aws_mqtt5_encoder_push_step_u16(struct aws_mqtt5_encoder *encoder, uint16_t value);

AWS_MQTT_API void aws_mqtt5_encoder_push_step_u32(struct aws_mqtt5_encoder *encoder, uint32_t value);

AWS_MQTT_API int aws_mqtt5_encoder_push_step_vli(struct aws_mqtt5_encoder *encoder, uint32_t value);

AWS_MQTT_API void aws_mqtt5_encoder_push_step_cursor(struct aws_mqtt5_encoder *encoder, struct aws_byte_cursor value);

AWS_MQTT_API size_t aws_mqtt5_compute_user_property_encode_length(
    const struct aws_mqtt5_user_property *properties,
    size_t user_property_count);

AWS_MQTT_API void aws_mqtt5_add_user_property_encoding_steps(
    struct aws_mqtt5_encoder *encoder,
    const struct aws_mqtt5_user_property *user_properties,
    size_t user_property_count);

AWS_EXTERN_C_END

/* macros to simplify encoding step list construction */

#define ADD_ENCODE_STEP_U8(encoder, value) aws_mqtt5_encoder_push_step_u8(encoder, (uint8_t)(value))
#define ADD_ENCODE_STEP_U16(encoder, value) aws_mqtt5_encoder_push_step_u16(encoder, (uint16_t)(value))
#define ADD_ENCODE_STEP_U32(encoder, value) aws_mqtt5_encoder_push_step_u32(encoder, (uint32_t)(value))
#define ADD_ENCODE_STEP_CURSOR(encoder, cursor) aws_mqtt5_encoder_push_step_cursor(encoder, (cursor))

#define ADD_ENCODE_STEP_VLI(encoder, value)                                                                            \
    if (aws_mqtt5_encoder_push_step_vli(encoder, (value))) {                                                           \
        return AWS_OP_ERR;                                                                                             \
    }

#define ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, cursor)                                                        \
    {                                                                                                                  \
        aws_mqtt5_encoder_push_step_u16(encoder, (uint16_t)((cursor).len));                                            \
        aws_mqtt5_encoder_push_step_cursor(encoder, (cursor));                                                         \
    }

#define ADD_ENCODE_STEP_OPTIONAL_LENGTH_PREFIXED_CURSOR(encoder, cursor_ptr)                                           \
    if (cursor_ptr != NULL) {                                                                                          \
        ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, *cursor_ptr);                                                  \
    }

/* Property-oriented macros for encode steps.  Properties have an additional prefix byte saying what their type is. */

#define ADD_ENCODE_STEP_OPTIONAL_U8_PROPERTY(encoder, property_value, value_ptr)                                       \
    if ((value_ptr) != NULL) {                                                                                         \
        ADD_ENCODE_STEP_U8(encoder, property_value);                                                                   \
        ADD_ENCODE_STEP_U8(encoder, *(value_ptr));                                                                     \
    }

#define ADD_ENCODE_STEP_OPTIONAL_U16_PROPERTY(encoder, property_value, value_ptr)                                      \
    if ((value_ptr) != NULL) {                                                                                         \
        ADD_ENCODE_STEP_U8(encoder, property_value);                                                                   \
        ADD_ENCODE_STEP_U16(encoder, *(value_ptr));                                                                    \
    }

#define ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(encoder, property_value, value_ptr)                                      \
    if ((value_ptr) != NULL) {                                                                                         \
        ADD_ENCODE_STEP_U8(encoder, property_value);                                                                   \
        ADD_ENCODE_STEP_U32(encoder, *(value_ptr));                                                                    \
    }

#define ADD_ENCODE_STEP_OPTIONAL_VLI_PROPERTY(encoder, property_value, value_ptr)                                      \
    if ((value_ptr) != NULL) {                                                                                         \
        ADD_ENCODE_STEP_U8(encoder, property_value);                                                                   \
        ADD_ENCODE_STEP_VLI(encoder, *(value_ptr));                                                                    \
    }

#define ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(encoder, property_type, cursor_ptr)                                   \
    if ((cursor_ptr) != NULL) {                                                                                        \
        ADD_ENCODE_STEP_U8(encoder, property_type);                                                                    \
        ADD_ENCODE_STEP_U16(encoder, (cursor_ptr)->len);                                                               \
        ADD_ENCODE_STEP_CURSOR(encoder, *(cursor_ptr));                                                                \
    }

/*
 * Macros to simplify packet size calculations, which are significantly complicated by mqtt5's many optional
 * properties.
 */

#define ADD_OPTIONAL_U8_PROPERTY_LENGTH(property_ptr, length)                                                          \
    if ((property_ptr) != NULL) {                                                                                      \
        (length) += 2;                                                                                                 \
    }

#define ADD_OPTIONAL_U16_PROPERTY_LENGTH(property_ptr, length)                                                         \
    if ((property_ptr) != NULL) {                                                                                      \
        (length) += 3;                                                                                                 \
    }

#define ADD_OPTIONAL_U32_PROPERTY_LENGTH(property_ptr, length)                                                         \
    if ((property_ptr) != NULL) {                                                                                      \
        (length) += 5;                                                                                                 \
    }

#define ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(property_ptr, length)                                                      \
    if ((property_ptr) != NULL) {                                                                                      \
        (length) += 3 + ((property_ptr)->len);                                                                         \
    }

#endif /* AWS_MQTT_MQTT5_ENCODER_H */