-
Notifications
You must be signed in to change notification settings - Fork 265
Expand file tree
/
Copy pathgtp_field.c
More file actions
332 lines (308 loc) · 12.7 KB
/
gtp_field.c
File metadata and controls
332 lines (308 loc) · 12.7 KB
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
#include <string.h>
#include "gtp_field.h"
#include "utils.h"
#include "os_print.h"
#include "shared_context.h" // strings
#include "app_mem_utils.h"
#include "mem_utils.h" // mem_buffer_cleanup
#include "lists.h"
#include "tlv_library.h"
#include "tlv_utils.h"
typedef union {
s_param_raw_context raw_ctx;
s_param_amount_context amount_ctx;
s_param_token_amount_context token_amount_ctx;
s_param_nft_context nft_ctx;
s_param_datetime_context datetime_ctx;
s_param_duration_context duration_ctx;
s_param_unit_context unit_ctx;
s_param_enum_context enum_ctx;
s_param_trusted_name_context trusted_name_ctx;
s_param_calldata_context calldata_ctx;
s_param_token_context token_ctx;
s_param_network_context network_ctx;
s_param_group_context group_ctx;
} u_param_context;
// Forward declarations
static bool handle_version(const tlv_data_t *data, s_field_ctx *context);
static bool handle_name(const tlv_data_t *data, s_field_ctx *context);
static bool handle_param_type(const tlv_data_t *data, s_field_ctx *context);
static bool handle_param(const tlv_data_t *data, s_field_ctx *context);
static bool handle_param_visible(const tlv_data_t *data, s_field_ctx *context);
static bool handle_param_constraint(const tlv_data_t *data, s_field_ctx *context);
static bool handle_separator(const tlv_data_t *data, s_field_ctx *context);
// Define TLV tags for Field
#define FIELD_TAGS(X) \
X(0x00, TAG_VERSION, handle_version, ENFORCE_UNIQUE_TAG) \
X(0x01, TAG_NAME, handle_name, ENFORCE_UNIQUE_TAG) \
X(0x02, TAG_PARAM_TYPE, handle_param_type, ENFORCE_UNIQUE_TAG) \
X(0x03, TAG_PARAM, handle_param, ENFORCE_UNIQUE_TAG) \
X(0x04, TAG_VISIBLE, handle_param_visible, ENFORCE_UNIQUE_TAG) \
X(0x05, TAG_CONSTRAINT, handle_param_constraint, ALLOW_MULTIPLE_TAG) \
X(0x06, TAG_SEPARATOR, handle_separator, ENFORCE_UNIQUE_TAG)
// Generate TLV parser for Field
DEFINE_TLV_PARSER(FIELD_TAGS, NULL, field_tlv_parser)
static bool handle_version(const tlv_data_t *data, s_field_ctx *context) {
return tlv_get_uint8_range(data, &context->field->version, 0, UINT8_MAX);
}
static bool handle_name(const tlv_data_t *data, s_field_ctx *context) {
str_cpy_explicit_trunc((const char *) data->value.ptr,
data->value.size,
context->field->name,
sizeof(context->field->name));
return true;
}
static bool handle_separator(const tlv_data_t *data, s_field_ctx *context) {
str_cpy_explicit_trunc((const char *) data->value.ptr,
data->value.size,
context->field->separator,
sizeof(context->field->separator));
return true;
}
static bool handle_param_type(const tlv_data_t *data, s_field_ctx *context) {
if (!tlv_get_uint8_range(data, &context->field->param_type, 0, UINT8_MAX)) {
return false;
}
switch (context->field->param_type) {
case PARAM_TYPE_RAW:
case PARAM_TYPE_AMOUNT:
case PARAM_TYPE_TOKEN_AMOUNT:
case PARAM_TYPE_NFT:
case PARAM_TYPE_DATETIME:
case PARAM_TYPE_DURATION:
case PARAM_TYPE_UNIT:
case PARAM_TYPE_ENUM:
case PARAM_TYPE_TRUSTED_NAME:
case PARAM_TYPE_CALLDATA:
case PARAM_TYPE_TOKEN:
case PARAM_TYPE_NETWORK:
case PARAM_TYPE_GROUP:
break;
default:
PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type);
return false;
}
return true;
}
static bool handle_param_visible(const tlv_data_t *data, s_field_ctx *context) {
e_param_visibility visibility = PARAM_VISIBILITY_ALWAYS;
// Set visibility
if (!tlv_get_uint8_range(data, (uint8_t *) &visibility, 0, PARAM_VISIBILITY_MAX - 1)) {
return false;
}
// Set visibility
context->field->visibility = visibility;
return true;
}
static bool handle_param_constraint(const tlv_data_t *data, s_field_ctx *context) {
// Check visibility presence
if (!TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_VISIBLE)) {
PRINTF("Error: No VISIBLE detected for the current FIELD struct!\n");
return false;
}
// Check visibility type
if (context->field->visibility == PARAM_VISIBILITY_ALWAYS) {
PRINTF("Error: CONSTRAINT present but VISIBLE is not MUST_BE or IF_NOT_IN!\n");
return false;
}
if (data->value.size == 0 || data->value.ptr == NULL) {
PRINTF("Error: Empty constraint value!\n");
return false;
}
// node->size is uint8_t; reject larger constraints rather than truncating
// (truncated size would later make the constraint-matching comparison
// silently always fail).
if (data->value.size > UINT8_MAX) {
PRINTF("Error: Constraint value too large (%d > %d)!\n", (int) data->value.size, UINT8_MAX);
return false;
}
// Allocate new constraint node
s_field_constraint *node = NULL;
if (APP_MEM_CALLOC((void **) &node, sizeof(s_field_constraint)) == false) {
PRINTF("Error: Failed to allocate memory for constraint node!\n");
return false;
}
node->size = (uint8_t) data->value.size;
// Allocate value buffer
if (APP_MEM_CALLOC((void **) &node->value, data->value.size) == false) {
PRINTF("Error: Failed to allocate memory for constraint value!\n");
APP_MEM_FREE(node);
return false;
}
memcpy(node->value, data->value.ptr, data->value.size);
// Add to linked list
flist_push_back((flist_node_t **) &context->field->constraints, (flist_node_t *) node);
return true;
}
static bool handle_param(const tlv_data_t *data, s_field_ctx *context) {
u_param_context param_ctx = {0};
bool ret;
// Check PARAM_TYPE presence
if (!TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_PARAM_TYPE)) {
PRINTF("Error: Received PARAM without a previous PARAM_TYPE!\n");
return false;
}
switch (context->field->param_type) {
case PARAM_TYPE_RAW:
param_ctx.raw_ctx.param = &context->field->param_raw;
ret = handle_param_raw_struct(&data->value, ¶m_ctx.raw_ctx);
break;
case PARAM_TYPE_AMOUNT:
param_ctx.amount_ctx.param = &context->field->param_amount;
ret = handle_param_amount_struct(&data->value, ¶m_ctx.amount_ctx);
break;
case PARAM_TYPE_TOKEN_AMOUNT:
param_ctx.token_amount_ctx.param = &context->field->param_token_amount;
ret = handle_param_token_amount_struct(&data->value, ¶m_ctx.token_amount_ctx);
break;
case PARAM_TYPE_NFT:
param_ctx.nft_ctx.param = &context->field->param_nft;
ret = handle_param_nft_struct(&data->value, ¶m_ctx.nft_ctx);
break;
case PARAM_TYPE_DATETIME:
param_ctx.datetime_ctx.param = &context->field->param_datetime;
ret = handle_param_datetime_struct(&data->value, ¶m_ctx.datetime_ctx);
break;
case PARAM_TYPE_DURATION:
param_ctx.duration_ctx.param = &context->field->param_duration;
ret = handle_param_duration_struct(&data->value, ¶m_ctx.duration_ctx);
break;
case PARAM_TYPE_UNIT:
param_ctx.unit_ctx.param = &context->field->param_unit;
ret = handle_param_unit_struct(&data->value, ¶m_ctx.unit_ctx);
break;
case PARAM_TYPE_ENUM:
param_ctx.enum_ctx.param = &context->field->param_enum;
ret = handle_param_enum_struct(&data->value, ¶m_ctx.enum_ctx);
break;
case PARAM_TYPE_TRUSTED_NAME:
param_ctx.trusted_name_ctx.param = &context->field->param_trusted_name;
ret = handle_param_trusted_name_struct(&data->value, ¶m_ctx.trusted_name_ctx);
break;
case PARAM_TYPE_CALLDATA:
param_ctx.calldata_ctx.param = &context->field->param_calldata;
ret = handle_param_calldata_struct(&data->value, ¶m_ctx.calldata_ctx);
break;
case PARAM_TYPE_TOKEN:
param_ctx.token_ctx.param = &context->field->param_token;
ret = handle_param_token_struct(&data->value, ¶m_ctx.token_ctx);
break;
case PARAM_TYPE_NETWORK:
param_ctx.network_ctx.param = &context->field->param_network;
ret = handle_param_network_struct(&data->value, ¶m_ctx.network_ctx);
break;
case PARAM_TYPE_GROUP:
param_ctx.group_ctx.param = &context->field->param_group;
ret = handle_param_group_struct(&data->value, ¶m_ctx.group_ctx);
break;
default:
return false;
}
return ret;
}
bool handle_field_struct(const buffer_t *buf, s_field_ctx *context) {
return field_tlv_parser(buf, context, &context->received_tags);
}
bool verify_field_struct(const s_field_ctx *context) {
// Check if struct version was provided
if (!TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_VERSION)) {
PRINTF("Error: no struct version specified!\n");
return false;
}
// Verify required fields based on version
switch (context->field->version) {
case 1:
if (!TLV_CHECK_RECEIVED_TAGS(context->received_tags,
TAG_VERSION,
TAG_NAME,
TAG_PARAM_TYPE,
TAG_PARAM)) {
PRINTF("Error: missing required field(s)\n");
return false;
}
break;
default:
PRINTF("Error: unsupported field struct version (%u)\n", context->field->version);
return false;
}
// Check optional visibility field
if (!TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_VISIBLE)) {
// Set default visibility if not provided
context->field->visibility = PARAM_VISIBILITY_ALWAYS;
}
return true;
}
bool format_field(s_field *field, uint8_t depth) {
bool ret;
switch (field->param_type) {
case PARAM_TYPE_RAW:
ret = format_param_raw(field);
break;
case PARAM_TYPE_AMOUNT:
ret = format_param_amount(&field->param_amount, field->name);
break;
case PARAM_TYPE_TOKEN_AMOUNT:
ret = format_param_token_amount(&field->param_token_amount, field->name);
break;
case PARAM_TYPE_NFT:
ret = format_param_nft(&field->param_nft, field->name);
break;
case PARAM_TYPE_DATETIME:
ret = format_param_datetime(&field->param_datetime, field->name);
break;
case PARAM_TYPE_DURATION:
ret = format_param_duration(&field->param_duration, field->name);
break;
case PARAM_TYPE_UNIT:
ret = format_param_unit(&field->param_unit, field->name);
break;
case PARAM_TYPE_ENUM:
ret = format_param_enum(&field->param_enum, field->name);
break;
case PARAM_TYPE_TRUSTED_NAME:
ret = format_param_trusted_name(field);
break;
case PARAM_TYPE_CALLDATA:
ret = format_param_calldata(&field->param_calldata, field->name);
break;
case PARAM_TYPE_TOKEN:
ret = format_param_token(&field->param_token, field->name);
break;
case PARAM_TYPE_NETWORK:
ret = format_param_network(&field->param_network, field->name);
break;
case PARAM_TYPE_GROUP:
ret = format_param_group(field, depth);
cleanup_param_group(&field->param_group);
break;
default:
ret = false;
}
// Cleanup constraints after formatting (they are no longer needed)
// This is safe to call even if constraints are NULL
cleanup_field_constraints(field);
// so that EIP-712 error-handling does trigger
strings.tmp.tmp[0] = '\0';
return ret;
}
void cleanup_field(s_field *field) {
if (field == NULL) {
return;
}
if (field->param_type == PARAM_TYPE_GROUP) {
cleanup_param_group(&field->param_group);
}
cleanup_field_constraints(field);
}
static void constraint_node_del(flist_node_t *node) {
if (node != NULL) {
s_field_constraint *constraint = (s_field_constraint *) node;
APP_MEM_FREE((void *) constraint->value);
APP_MEM_FREE((void *) constraint);
}
}
void cleanup_field_constraints(s_field *field) {
if (field != NULL && field->constraints != NULL) {
flist_clear((flist_node_t **) &field->constraints, constraint_node_del);
}
}