blob: 0426d83d2cc94ab5147c537e77f14479db7b76e3 [file] [log] [blame]
Frédéric Lécaille1fceee82019-02-25 15:30:36 +01001/*
2 * include/proto/protocol_buffers.h
3 * This file contains functions and macros declarations for protocol buffers decoding.
4 *
5 * Copyright 2012 Willy Tarreau <w@1wt.eu>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation, version 2.1
10 * exclusively.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _PROTO_PROTOCOL_BUFFERS_H
23#define _PROTO_PROTOCOL_BUFFERS_H
24
Willy Tarreaua1bd1fa2019-03-29 17:26:33 +010025#include <inttypes.h>
Frédéric Lécaille5f33f852019-03-06 08:03:44 +010026#include <types/arg.h>
Frédéric Lécaille1fceee82019-02-25 15:30:36 +010027#include <types/protocol_buffers.h>
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010028#include <proto/sample.h>
Frédéric Lécaille1fceee82019-02-25 15:30:36 +010029
30#define PBUF_VARINT_DONT_STOP_BIT 7
31#define PBUF_VARINT_DONT_STOP_BITMASK (1 << PBUF_VARINT_DONT_STOP_BIT)
32#define PBUF_VARINT_DATA_BITMASK ~PBUF_VARINT_DONT_STOP_BITMASK
33
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010034/* .skip and .smp_store prototypes. */
35int protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen);
Frédéric Lécaille756d97f2019-03-04 19:03:48 +010036int protobuf_smp_store_varint(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010037 unsigned char *pos, size_t len, size_t vlen);
38int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen);
Frédéric Lécaille756d97f2019-03-04 19:03:48 +010039int protobuf_smp_store_64bit(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010040 unsigned char *pos, size_t len, size_t vlen);
41int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen);
Frédéric Lécaille756d97f2019-03-04 19:03:48 +010042int protobuf_smp_store_vlen(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010043 unsigned char *pos, size_t len, size_t vlen);
44int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen);
Frédéric Lécaille756d97f2019-03-04 19:03:48 +010045int protobuf_smp_store_32bit(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +010046 unsigned char *pos, size_t len, size_t vlen);
47
48struct protobuf_parser_def protobuf_parser_defs [] = {
49 [PBUF_TYPE_VARINT ] = {
50 .skip = protobuf_skip_varint,
51 .smp_store = protobuf_smp_store_varint,
52 },
53 [PBUF_TYPE_64BIT ] = {
54 .skip = protobuf_skip_64bit,
55 .smp_store = protobuf_smp_store_64bit,
56 },
57 [PBUF_TYPE_LENGTH_DELIMITED] = {
58 .skip = protobuf_skip_vlen,
59 .smp_store = protobuf_smp_store_vlen,
60 },
61 [PBUF_TYPE_START_GROUP ] = {
62 /* XXX Deprecated XXX */
63 },
64 [PBUF_TYPE_STOP_GROUP ] = {
65 /* XXX Deprecated XXX */
66 },
67 [PBUF_TYPE_32BIT ] = {
68 .skip = protobuf_skip_32bit,
69 .smp_store = protobuf_smp_store_32bit,
70 },
71};
72
Frédéric Lécaille1fceee82019-02-25 15:30:36 +010073/*
Frédéric Lécaille756d97f2019-03-04 19:03:48 +010074 * Note that the field values with protocol buffers 32bit and 64bit fixed size as type
75 * are sent in little-endian byte order to the network.
76 */
77
78/* Convert a little-endian ordered 32bit integer to the byte order of the host. */
79static inline uint32_t pbuf_le32toh(uint32_t v)
80{
81 uint8_t *p = (uint8_t *)&v;
82 return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
83}
84
85/* Convert a little-endian ordered 64bit integer to the byte order of the host. */
86static inline uint64_t pbuf_le64toh(uint64_t v)
87{
88 return (uint64_t)(pbuf_le32toh(v >> 32)) << 32 | pbuf_le32toh(v);
89}
90
91/*
92 * Return a protobuf type enum from <s> string if succedeed, -1 if not.
93 */
94int protobuf_type(const char *s)
95{
96 /* varint types. */
97 if (!strcmp(s, "int32"))
98 return PBUF_T_VARINT_INT32;
99 else if (!strcmp(s, "uint32"))
100 return PBUF_T_VARINT_UINT32;
101 else if (!strcmp(s, "sint32"))
102 return PBUF_T_VARINT_SINT32;
103 else if (!strcmp(s, "int64"))
104 return PBUF_T_VARINT_INT64;
105 else if (!strcmp(s, "uint64"))
106 return PBUF_T_VARINT_UINT64;
107 else if (!strcmp(s, "sint64"))
108 return PBUF_T_VARINT_SINT64;
109 else if (!strcmp(s, "bool"))
110 return PBUF_T_VARINT_BOOL;
111 else if (!strcmp(s, "enum"))
112 return PBUF_T_VARINT_ENUM;
113
114 /* 32bit fixed size types. */
115 else if (!strcmp(s, "fixed32"))
116 return PBUF_T_32BIT_FIXED32;
117 else if (!strcmp(s, "sfixed32"))
118 return PBUF_T_32BIT_SFIXED32;
119 else if (!strcmp(s, "float"))
120 return PBUF_T_32BIT_FLOAT;
121
122 /* 64bit fixed size types. */
123 else if (!strcmp(s, "fixed64"))
124 return PBUF_T_64BIT_FIXED64;
125 else if (!strcmp(s, "sfixed64"))
126 return PBUF_T_64BIT_SFIXED64;
127 else if (!strcmp(s, "double"))
128 return PBUF_T_64BIT_DOUBLE;
129 else
130 return -1;
131}
132
133/*
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100134 * Decode a protocol buffers varint located in a buffer at <pos> address with
135 * <len> as length. The decoded value is stored at <val>.
136 * Returns 1 if succeeded, 0 if not.
137 */
138static inline int
139protobuf_varint(uint64_t *val, unsigned char *pos, size_t len)
140{
141 unsigned int shift;
142
143 *val = 0;
144 shift = 0;
145
146 while (len > 0) {
147 int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
148
149 *val |= ((uint64_t)(*pos & PBUF_VARINT_DATA_BITMASK)) << shift;
150
151 ++pos;
152 --len;
153
154 if (stop)
155 break;
156 else if (!len)
157 return 0;
158
159 shift += 7;
160 /* The maximum length in bytes of a 64-bit encoded value is 10. */
Frédéric Lécaille876ed552020-04-02 14:24:31 +0200161 if (shift > 63)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100162 return 0;
163 }
164
165 return 1;
166}
167
168/*
169 * Decode a protocol buffers varint located in a buffer at <pos> offset address with
170 * <len> as length address. Update <pos> and <len> consequently. Decrease <*len>
171 * by the number of decoded bytes. The decoded value is stored at <val>.
172 * Returns 1 if succeeded, 0 if not.
173 */
174static inline int
175protobuf_decode_varint(uint64_t *val, unsigned char **pos, size_t *len)
176{
177 unsigned int shift;
178
179 *val = 0;
180 shift = 0;
181
182 while (*len > 0) {
183 int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
184
185 *val |= ((uint64_t)**pos & PBUF_VARINT_DATA_BITMASK) << shift;
186
187 ++*pos;
188 --*len;
189
190 if (stop)
191 break;
192 else if (!*len)
193 return 0;
194
195 shift += 7;
196 /* The maximum length in bytes of a 64-bit encoded value is 10. */
Frédéric Lécaille876ed552020-04-02 14:24:31 +0200197 if (shift > 63)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100198 return 0;
199 }
200
201 return 1;
202}
203
204/*
205 * Skip a protocol buffer varint found at <pos> as position address with <len>
206 * as available length address. Update <*pos> to make it point to the next
207 * available byte. Decrease <*len> by the number of skipped bytes.
208 * Returns 1 if succeeded, 0 if not.
209 */
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100210int
211protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100212{
213 unsigned int shift;
214
215 shift = 0;
216
217 while (*len > 0) {
218 int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
219
220 ++*pos;
221 --*len;
222
223 if (stop)
224 break;
225 else if (!*len)
226 return 0;
227
228 shift += 7;
229 /* The maximum length in bytes of a 64-bit encoded value is 10. */
Frédéric Lécaille876ed552020-04-02 14:24:31 +0200230 if (shift > 63)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100231 return 0;
232 }
233
234 return 1;
235}
236
237/*
238 * If succeeded, return the length of a prococol buffers varint found at <pos> as
239 * position address, with <len> as address of the available bytes at <*pos>.
240 * Update <*pos> to make it point to the next available byte. Decrease <*len>
241 * by the number of bytes used to encode this varint.
242 * Return -1 if failed.
243 */
244static inline int
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100245protobuf_varint_getlen(unsigned char *pos, size_t len)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100246{
247 unsigned char *spos;
248 unsigned int shift;
249
250 shift = 0;
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100251 spos = pos;
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100252
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100253 while (len > 0) {
254 int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100255
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100256 ++pos;
257 --len;
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100258
259 if (stop)
260 break;
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100261 else if (!len)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100262 return -1;
263
264 shift += 7;
265 /* The maximum length in bytes of a 64-bit encoded value is 10. */
Frédéric Lécaille876ed552020-04-02 14:24:31 +0200266 if (shift > 63)
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100267 return -1;
268 }
269
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100270 return pos - spos;
271}
272
273/*
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100274 * Store a varint field value in a sample from <pos> buffer
275 * with <len> available bytes after having decoded it if needed
276 * depending on <type> the expected protocol buffer type of the field.
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100277 * Return 1 if succeeded, 0 if not.
278 */
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100279int protobuf_smp_store_varint(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100280 unsigned char *pos, size_t len, size_t vlen)
281{
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100282 switch (type) {
283 case PBUF_T_BINARY:
284 {
285 int varint_len;
286
287 varint_len = protobuf_varint_getlen(pos, len);
288 if (varint_len == -1)
289 return 0;
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100290
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100291 smp->data.type = SMP_T_BIN;
292 smp->data.u.str.area = (char *)pos;
293 smp->data.u.str.data = varint_len;
294 smp->flags = SMP_F_VOL_TEST;
295 break;
296 }
297
298 case PBUF_T_VARINT_INT32 ... PBUF_T_VARINT_ENUM:
299 {
300 uint64_t varint;
301
302 if (!protobuf_varint(&varint, pos, len))
303 return 0;
304
305 smp->data.u.sint = varint;
306 smp->data.type = SMP_T_SINT;
307 break;
308 }
309
310 case PBUF_T_VARINT_SINT32 ... PBUF_T_VARINT_SINT64:
311 {
312 uint64_t varint;
313
314 if (!protobuf_varint(&varint, pos, len))
315 return 0;
316
317 /* zigzag decoding. */
318 smp->data.u.sint = (varint >> 1) ^ -(varint & 1);
319 smp->data.type = SMP_T_SINT;
320 break;
321 }
322
323 default:
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100324 return 0;
325
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100326 }
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100327
328 return 1;
329}
330
331/*
332 * Move forward <*pos> buffer by 8 bytes. Used to skip a 64bit field.
333 */
334int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen)
335{
336 if (*len < sizeof(uint64_t))
337 return 0;
338
339 *pos += sizeof(uint64_t);
340 *len -= sizeof(uint64_t);
341
342 return 1;
343}
344
345/*
346 * Store a fixed size 64bit field value in a sample from <pos> buffer
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100347 * with <len> available bytes after having decoded it depending on <type>
348 * the expected protocol buffer type of the field.
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100349 * Return 1 if succeeded, 0 if not.
350 */
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100351int protobuf_smp_store_64bit(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100352 unsigned char *pos, size_t len, size_t vlen)
353{
354 if (len < sizeof(uint64_t))
355 return 0;
356
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100357 switch (type) {
358 case PBUF_T_BINARY:
359 smp->data.type = SMP_T_BIN;
360 smp->data.u.str.area = (char *)pos;
361 smp->data.u.str.data = sizeof(uint64_t);
362 smp->flags = SMP_F_VOL_TEST;
363 break;
364
365 case PBUF_T_64BIT_FIXED64:
366 case PBUF_T_64BIT_SFIXED64:
367 smp->data.type = SMP_T_SINT;
368 smp->data.u.sint = pbuf_le64toh(*(uint64_t *)pos);
369 smp->flags = SMP_F_VOL_TEST;
370 break;
371
372 case PBUF_T_64BIT_DOUBLE:
373 smp->data.type = SMP_T_SINT;
374 smp->data.u.sint = pbuf_le64toh(*(double *)pos);
375 smp->flags = SMP_F_VOL_TEST;
376 break;
377
378 default:
379 return 0;
380 }
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100381
382 return 1;
383}
384
385/*
386 * Move forward <*pos> buffer by <vlen> bytes. Use to skip a length-delimited
387 * field.
388 */
389int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen)
390{
391 if (*len < vlen)
392 return 0;
393
394 *pos += vlen;
395 *len -= vlen;
396
397 return 1;
398}
399
400/*
401 * Store a <vlen>-bytes length-delimited field value in a sample from <pos>
402 * buffer with <len> available bytes.
403 * Return 1 if succeeded, 0 if not.
404 */
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100405int protobuf_smp_store_vlen(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100406 unsigned char *pos, size_t len, size_t vlen)
407{
408 if (len < vlen)
409 return 0;
410
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100411 if (type != PBUF_T_BINARY)
412 return 0;
413
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100414 smp->data.type = SMP_T_BIN;
415 smp->data.u.str.area = (char *)pos;
416 smp->data.u.str.data = vlen;
417 smp->flags = SMP_F_VOL_TEST;
418
419 return 1;
420}
421
422/*
423 * Move forward <*pos> buffer by 4 bytes. Used to skip a 32bit field.
424 */
425int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen)
426{
427 if (*len < sizeof(uint32_t))
428 return 0;
429
430 *pos += sizeof(uint32_t);
431 *len -= sizeof(uint32_t);
432
433 return 1;
434}
435
436/*
437 * Store a fixed size 32bit field value in a sample from <pos> buffer
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100438 * with <len> available bytes after having decoded it depending on <type>
439 * the expected protocol buffer type of the field.
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100440 * Return 1 if succeeded, 0 if not.
441 */
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100442int protobuf_smp_store_32bit(struct sample *smp, int type,
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100443 unsigned char *pos, size_t len, size_t vlen)
444{
445 if (len < sizeof(uint32_t))
446 return 0;
447
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100448 switch (type) {
449 case PBUF_T_BINARY:
450 smp->data.type = SMP_T_BIN;
451 smp->data.u.str.area = (char *)pos;
452 smp->data.u.str.data = sizeof(uint32_t);
453 smp->flags = SMP_F_VOL_TEST;
454 break;
455
456 case PBUF_T_32BIT_FIXED32:
457 smp->data.type = SMP_T_SINT;
458 smp->data.u.sint = pbuf_le32toh(*(uint32_t *)pos);
459 smp->flags = SMP_F_VOL_TEST;
460 break;
461
462 case PBUF_T_32BIT_SFIXED32:
463 smp->data.type = SMP_T_SINT;
464 smp->data.u.sint = (int32_t)pbuf_le32toh(*(uint32_t *)pos);
465 smp->flags = SMP_F_VOL_TEST;
466 break;
467
468 case PBUF_T_32BIT_FLOAT:
469 smp->data.type = SMP_T_SINT;
470 smp->data.u.sint = pbuf_le32toh(*(float *)pos);
471 smp->flags = SMP_F_VOL_TEST;
472 break;
473
474 default:
475 return 0;
476 }
Frédéric Lécaille7c93e882019-03-04 07:33:41 +0100477
478 return 1;
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100479}
480
Frédéric Lécaille5f33f852019-03-06 08:03:44 +0100481/*
482 * Lookup for a protocol buffers field whose parameters are provided by <arg_p>
483 * first argument in the buffer with <pos> as address and <len> as length address.
484 * If found, store its value depending on the type of storage to use provided by <arg_p>
485 * second argument and return 1, 0 if not.
486 */
487static inline int protobuf_field_lookup(const struct arg *arg_p, struct sample *smp,
488 unsigned char **pos, size_t *len)
489{
490 unsigned int *fid;
491 size_t fid_sz;
492 int type;
493 uint64_t elen;
494 int field;
495
496 fid = arg_p[0].data.fid.ids;
497 fid_sz = arg_p[0].data.fid.sz;
498 type = arg_p[1].data.sint;
499
500 /* Length of the length-delimited messages if any. */
501 elen = 0;
502 field = 0;
503
504 while (field < fid_sz) {
505 int found;
506 uint64_t key, sleft;
507 struct protobuf_parser_def *pbuf_parser = NULL;
508 unsigned int wire_type, field_number;
509
510 if ((ssize_t)*len <= 0)
511 return 0;
512
513 /* Remaining bytes saving. */
514 sleft = *len;
515
516 /* Key decoding */
517 if (!protobuf_decode_varint(&key, pos, len))
518 return 0;
519
520 wire_type = key & 0x7;
521 field_number = key >> 3;
522 found = field_number == fid[field];
523
524 /* Skip the data if the current field does not match. */
525 switch (wire_type) {
526 case PBUF_TYPE_VARINT:
527 case PBUF_TYPE_32BIT:
528 case PBUF_TYPE_64BIT:
529 pbuf_parser = &protobuf_parser_defs[wire_type];
530 if (!found && !pbuf_parser->skip(pos, len, 0))
531 return 0;
532 break;
533
534 case PBUF_TYPE_LENGTH_DELIMITED:
535 /* Decode the length of this length-delimited field. */
536 if (!protobuf_decode_varint(&elen, pos, len) || elen > *len)
537 return 0;
538
539 /* The size of the current field is computed from here to skip
540 * the bytes used to encode the previous length.*
541 */
542 sleft = *len;
543 pbuf_parser = &protobuf_parser_defs[wire_type];
544 if (!found && !pbuf_parser->skip(pos, len, elen))
545 return 0;
546 break;
547
548 default:
549 return 0;
550 }
551
552 /* Store the data if found. Note that <pbuf_parser> is not NULL */
553 if (found && field == fid_sz - 1)
554 return pbuf_parser->smp_store(smp, type, *pos, *len, elen);
555
556 if ((ssize_t)(elen) > 0)
557 elen -= sleft - *len;
558
559 if (found) {
560 field++;
561 }
562 else if ((ssize_t)elen <= 0) {
563 field = 0;
564 }
565 }
566
567 return 0;
568}
569
Frédéric Lécaille1fceee82019-02-25 15:30:36 +0100570#endif /* _PROTO_PROTOCOL_BUFFERS_H */
571
572/*
573 * Local variables:
574 * c-indent-level: 8
575 * c-basic-offset: 8
576 * End:
577 */