Coverage Report

Created: 2023-06-07 06:12

/src/haproxy/include/haproxy/protobuf.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * include/haproxy/protobuf.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 _HAPROXY_PROTOBUF_H
23
#define _HAPROXY_PROTOBUF_H
24
25
#include <haproxy/api-t.h>
26
#include <haproxy/arg-t.h>
27
#include <haproxy/protobuf-t.h>
28
#include <haproxy/sample-t.h>
29
30
0
#define PBUF_VARINT_DONT_STOP_BIT       7
31
0
#define PBUF_VARINT_DONT_STOP_BITMASK  (1 << PBUF_VARINT_DONT_STOP_BIT)
32
0
#define PBUF_VARINT_DATA_BITMASK            ~PBUF_VARINT_DONT_STOP_BITMASK
33
34
/* .skip and .smp_store prototypes. */
35
int protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen);
36
int protobuf_smp_store_varint(struct sample *smp, int type,
37
                              unsigned char *pos, size_t len, size_t vlen);
38
int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen);
39
int protobuf_smp_store_64bit(struct sample *smp, int type,
40
                             unsigned char *pos, size_t len, size_t vlen);
41
int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen);
42
int protobuf_smp_store_vlen(struct sample *smp, int type,
43
                            unsigned char *pos, size_t len, size_t vlen);
44
int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen);
45
int protobuf_smp_store_32bit(struct sample *smp, int type,
46
                             unsigned char *pos, size_t len, size_t vlen);
47
48
struct 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
73
/*
74
 * 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. */
79
static inline uint32_t pbuf_le32toh(uint32_t v)
80
0
{
81
0
  uint8_t *p = (uint8_t *)&v;
82
0
  return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
83
0
}
84
85
/* Convert a little-endian ordered 64bit integer to the byte order of the host. */
86
static inline uint64_t pbuf_le64toh(uint64_t v)
87
0
{
88
0
  return (uint64_t)(pbuf_le32toh(v >> 32)) << 32 | pbuf_le32toh(v);
89
0
}
90
91
/*
92
 * Return a protobuf type enum from <s> string if succedeed, -1 if not.
93
 */
94
int protobuf_type(const char *s)
95
0
{
96
  /* varint types. */
97
0
  if (strcmp(s, "int32") == 0)
98
0
    return PBUF_T_VARINT_INT32;
99
0
  else if (strcmp(s, "uint32") == 0)
100
0
    return PBUF_T_VARINT_UINT32;
101
0
  else if (strcmp(s, "sint32") == 0)
102
0
    return PBUF_T_VARINT_SINT32;
103
0
  else if (strcmp(s, "int64") == 0)
104
0
    return PBUF_T_VARINT_INT64;
105
0
  else if (strcmp(s, "uint64") == 0)
106
0
    return PBUF_T_VARINT_UINT64;
107
0
  else if (strcmp(s, "sint64") == 0)
108
0
    return PBUF_T_VARINT_SINT64;
109
0
  else if (strcmp(s, "bool") == 0)
110
0
    return PBUF_T_VARINT_BOOL;
111
0
  else if (strcmp(s, "enum") == 0)
112
0
    return PBUF_T_VARINT_ENUM;
113
114
  /* 32bit fixed size types. */
115
0
  else if (strcmp(s, "fixed32") == 0)
116
0
    return PBUF_T_32BIT_FIXED32;
117
0
  else if (strcmp(s, "sfixed32") == 0)
118
0
    return PBUF_T_32BIT_SFIXED32;
119
0
  else if (strcmp(s, "float") == 0)
120
0
    return PBUF_T_32BIT_FLOAT;
121
122
  /* 64bit fixed size types. */
123
0
  else if (strcmp(s, "fixed64") == 0)
124
0
    return PBUF_T_64BIT_FIXED64;
125
0
  else if (strcmp(s, "sfixed64") == 0)
126
0
    return PBUF_T_64BIT_SFIXED64;
127
0
  else if (strcmp(s, "double") == 0)
128
0
    return PBUF_T_64BIT_DOUBLE;
129
0
  else
130
0
    return -1;
131
0
}
132
133
/*
134
 * 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
 */
138
static inline int
139
protobuf_varint(uint64_t *val, unsigned char *pos, size_t len)
140
0
{
141
0
  unsigned int shift;
142
143
0
  *val = 0;
144
0
  shift = 0;
145
146
0
  while (len > 0) {
147
0
    int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
148
149
0
    *val |= ((uint64_t)(*pos & PBUF_VARINT_DATA_BITMASK)) << shift;
150
151
0
    ++pos;
152
0
    --len;
153
154
0
    if (stop)
155
0
      break;
156
0
    else if (!len)
157
0
      return 0;
158
159
0
    shift += 7;
160
    /* The maximum length in bytes of a 64-bit encoded value is 10. */
161
0
    if (shift > 63)
162
0
      return 0;
163
0
  }
164
165
0
  return 1;
166
0
}
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
 */
174
static inline int
175
protobuf_decode_varint(uint64_t *val, unsigned char **pos, size_t *len)
176
0
{
177
0
  unsigned int shift;
178
179
0
  *val = 0;
180
0
  shift = 0;
181
182
0
  while (*len > 0) {
183
0
    int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
184
185
0
    *val |= ((uint64_t)**pos & PBUF_VARINT_DATA_BITMASK) << shift;
186
187
0
    ++*pos;
188
0
    --*len;
189
190
0
    if (stop)
191
0
      break;
192
0
    else if (!*len)
193
0
      return 0;
194
195
0
    shift += 7;
196
    /* The maximum length in bytes of a 64-bit encoded value is 10. */
197
0
    if (shift > 63)
198
0
      return 0;
199
0
  }
200
201
0
  return 1;
202
0
}
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
 */
210
int
211
protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen)
212
0
{
213
0
  unsigned int shift;
214
215
0
  shift = 0;
216
217
0
  while (*len > 0) {
218
0
    int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
219
220
0
    ++*pos;
221
0
    --*len;
222
223
0
    if (stop)
224
0
      break;
225
0
    else if (!*len)
226
0
      return 0;
227
228
0
    shift += 7;
229
    /* The maximum length in bytes of a 64-bit encoded value is 10. */
230
0
    if (shift > 63)
231
0
      return 0;
232
0
  }
233
234
0
  return 1;
235
0
}
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
 */
244
static inline int
245
protobuf_varint_getlen(unsigned char *pos, size_t len)
246
0
{
247
0
  unsigned char *spos;
248
0
  unsigned int shift;
249
250
0
  shift = 0;
251
0
  spos = pos;
252
253
0
  while (len > 0) {
254
0
    int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
255
256
0
    ++pos;
257
0
    --len;
258
259
0
    if (stop)
260
0
      break;
261
0
    else if (!len)
262
0
      return -1;
263
264
0
    shift += 7;
265
    /* The maximum length in bytes of a 64-bit encoded value is 10. */
266
0
    if (shift > 63)
267
0
      return -1;
268
0
  }
269
270
0
  return pos - spos;
271
0
}
272
273
/*
274
 * 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.
277
 * Return 1 if succeeded, 0 if not.
278
 */
279
int protobuf_smp_store_varint(struct sample *smp, int type,
280
                              unsigned char *pos, size_t len, size_t vlen)
281
0
{
282
0
  switch (type) {
283
0
  case PBUF_T_BINARY:
284
0
  {
285
0
    int varint_len;
286
287
0
    varint_len = protobuf_varint_getlen(pos, len);
288
0
    if (varint_len == -1)
289
0
      return 0;
290
291
0
    smp->data.type = SMP_T_BIN;
292
0
    smp->data.u.str.area = (char *)pos;
293
0
    smp->data.u.str.data = varint_len;
294
0
    smp->flags = SMP_F_VOL_TEST;
295
0
    break;
296
0
  }
297
298
0
  case PBUF_T_VARINT_INT32 ... PBUF_T_VARINT_ENUM:
299
0
  {
300
0
    uint64_t varint;
301
302
0
    if (!protobuf_varint(&varint, pos, len))
303
0
      return 0;
304
305
0
    smp->data.u.sint = varint;
306
0
    smp->data.type = SMP_T_SINT;
307
0
    break;
308
0
  }
309
310
0
  case PBUF_T_VARINT_SINT32 ... PBUF_T_VARINT_SINT64:
311
0
  {
312
0
    uint64_t varint;
313
314
0
    if (!protobuf_varint(&varint, pos, len))
315
0
      return 0;
316
317
    /* zigzag decoding. */
318
0
    smp->data.u.sint = (varint >> 1) ^ -(varint & 1);
319
0
    smp->data.type = SMP_T_SINT;
320
0
    break;
321
0
  }
322
323
0
  default:
324
0
    return 0;
325
326
0
  }
327
328
0
  return 1;
329
0
}
330
331
/*
332
 * Move forward <*pos> buffer by 8 bytes. Used to skip a 64bit field.
333
 */
334
int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen)
335
0
{
336
0
  if (*len < sizeof(uint64_t))
337
0
      return 0;
338
339
0
  *pos += sizeof(uint64_t);
340
0
  *len -= sizeof(uint64_t);
341
342
0
  return 1;
343
0
}
344
345
/*
346
 * Store a fixed size 64bit field value in a sample from <pos> buffer
347
 * with <len> available bytes after having decoded it depending on <type>
348
 * the expected protocol buffer type of the field.
349
 * Return 1 if succeeded, 0 if not.
350
 */
351
int protobuf_smp_store_64bit(struct sample *smp, int type,
352
                             unsigned char *pos, size_t len, size_t vlen)
353
0
{
354
0
  if (len < sizeof(uint64_t))
355
0
      return 0;
356
357
0
  switch (type) {
358
0
  case PBUF_T_BINARY:
359
0
    smp->data.type = SMP_T_BIN;
360
0
    smp->data.u.str.area = (char *)pos;
361
0
    smp->data.u.str.data = sizeof(uint64_t);
362
0
    smp->flags = SMP_F_VOL_TEST;
363
0
    break;
364
365
0
  case PBUF_T_64BIT_FIXED64:
366
0
  case PBUF_T_64BIT_SFIXED64:
367
0
    smp->data.type = SMP_T_SINT;
368
0
    smp->data.u.sint = pbuf_le64toh(*(uint64_t *)pos);
369
0
    smp->flags = SMP_F_VOL_TEST;
370
0
    break;
371
372
0
  case PBUF_T_64BIT_DOUBLE:
373
0
    smp->data.type = SMP_T_SINT;
374
0
    smp->data.u.sint = pbuf_le64toh(*(double *)pos);
375
0
    smp->flags = SMP_F_VOL_TEST;
376
0
    break;
377
378
0
  default:
379
0
    return 0;
380
0
  }
381
382
0
  return 1;
383
0
}
384
385
/*
386
 * Move forward <*pos> buffer by <vlen> bytes. Use to skip a length-delimited
387
 * field.
388
 */
389
int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen)
390
0
{
391
0
  if (*len < vlen)
392
0
    return 0;
393
394
0
  *pos += vlen;
395
0
  *len -= vlen;
396
397
0
  return 1;
398
0
}
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
 */
405
int protobuf_smp_store_vlen(struct sample *smp, int type,
406
                            unsigned char *pos, size_t len, size_t vlen)
407
0
{
408
0
  if (len < vlen)
409
0
    return 0;
410
411
0
  if (type != PBUF_T_BINARY)
412
0
    return 0;
413
414
0
  smp->data.type = SMP_T_BIN;
415
0
  smp->data.u.str.area = (char *)pos;
416
0
  smp->data.u.str.data = vlen;
417
0
  smp->flags = SMP_F_VOL_TEST;
418
419
0
  return 1;
420
0
}
421
422
/*
423
 * Move forward <*pos> buffer by 4 bytes. Used to skip a 32bit field.
424
 */
425
int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen)
426
0
{
427
0
  if (*len < sizeof(uint32_t))
428
0
      return 0;
429
430
0
  *pos += sizeof(uint32_t);
431
0
  *len -= sizeof(uint32_t);
432
433
0
  return 1;
434
0
}
435
436
/*
437
 * Store a fixed size 32bit field value in a sample from <pos> buffer
438
 * with <len> available bytes after having decoded it depending on <type>
439
 * the expected protocol buffer type of the field.
440
 * Return 1 if succeeded, 0 if not.
441
 */
442
int protobuf_smp_store_32bit(struct sample *smp, int type,
443
                             unsigned char *pos, size_t len, size_t vlen)
444
0
{
445
0
  if (len < sizeof(uint32_t))
446
0
      return 0;
447
448
0
  switch (type) {
449
0
  case PBUF_T_BINARY:
450
0
    smp->data.type = SMP_T_BIN;
451
0
    smp->data.u.str.area = (char *)pos;
452
0
    smp->data.u.str.data = sizeof(uint32_t);
453
0
    smp->flags = SMP_F_VOL_TEST;
454
0
    break;
455
456
0
  case PBUF_T_32BIT_FIXED32:
457
0
    smp->data.type = SMP_T_SINT;
458
0
    smp->data.u.sint = pbuf_le32toh(*(uint32_t *)pos);
459
0
    smp->flags = SMP_F_VOL_TEST;
460
0
    break;
461
462
0
  case PBUF_T_32BIT_SFIXED32:
463
0
    smp->data.type = SMP_T_SINT;
464
0
    smp->data.u.sint = (int32_t)pbuf_le32toh(*(uint32_t *)pos);
465
0
    smp->flags = SMP_F_VOL_TEST;
466
0
    break;
467
468
0
  case PBUF_T_32BIT_FLOAT:
469
0
    smp->data.type = SMP_T_SINT;
470
0
    smp->data.u.sint = pbuf_le32toh(*(float *)pos);
471
0
    smp->flags = SMP_F_VOL_TEST;
472
0
    break;
473
474
0
  default:
475
0
    return 0;
476
0
  }
477
478
0
  return 1;
479
0
}
480
481
/*
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
 */
487
static inline int protobuf_field_lookup(const struct arg *arg_p, struct sample *smp,
488
                                        unsigned char **pos, size_t *len)
489
0
{
490
0
  unsigned int *fid;
491
0
  size_t fid_sz;
492
0
  int type;
493
0
  uint64_t elen;
494
0
  int field;
495
496
0
  fid = arg_p[0].data.fid.ids;
497
0
  fid_sz = arg_p[0].data.fid.sz;
498
0
  type = arg_p[1].data.sint;
499
500
  /* Length of the length-delimited messages if any. */
501
0
  elen = 0;
502
0
  field = 0;
503
504
0
  while (field < fid_sz) {
505
0
    int found;
506
0
    uint64_t key, sleft;
507
0
    struct protobuf_parser_def *pbuf_parser = NULL;
508
0
    unsigned int wire_type, field_number;
509
510
0
    if ((ssize_t)*len <= 0)
511
0
      return 0;
512
513
    /* Remaining bytes saving. */
514
0
    sleft = *len;
515
516
    /* Key decoding */
517
0
    if (!protobuf_decode_varint(&key, pos, len))
518
0
      return 0;
519
520
0
    wire_type = key & 0x7;
521
0
    field_number = key >> 3;
522
0
    found = field_number == fid[field];
523
524
    /* Skip the data if the current field does not match. */
525
0
    switch (wire_type) {
526
0
    case PBUF_TYPE_VARINT:
527
0
    case PBUF_TYPE_32BIT:
528
0
    case PBUF_TYPE_64BIT:
529
0
      pbuf_parser = &protobuf_parser_defs[wire_type];
530
0
      if (!found && !pbuf_parser->skip(pos, len, 0))
531
0
        return 0;
532
0
      break;
533
534
0
    case PBUF_TYPE_LENGTH_DELIMITED:
535
      /* Decode the length of this length-delimited field. */
536
0
      if (!protobuf_decode_varint(&elen, pos, len) || elen > *len)
537
0
        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
0
      sleft = *len;
543
0
      pbuf_parser = &protobuf_parser_defs[wire_type];
544
0
      if (!found && !pbuf_parser->skip(pos, len, elen))
545
0
        return 0;
546
0
      break;
547
548
0
    default:
549
0
      return 0;
550
0
    }
551
552
    /* Store the data if found. Note that <pbuf_parser> is not NULL */
553
0
    if (found && field == fid_sz - 1)
554
0
      return pbuf_parser->smp_store(smp, type, *pos, *len, elen);
555
556
0
    if ((ssize_t)(elen) > 0)
557
0
      elen -= sleft - *len;
558
559
0
    if (found) {
560
0
      field++;
561
0
    }
562
0
    else if ((ssize_t)elen <= 0) {
563
0
      field = 0;
564
0
    }
565
0
  }
566
567
0
  return 0;
568
0
}
569
570
#endif /* _HAPROXY_PROTOBUF_H */
571
572
/*
573
 * Local variables:
574
 *  c-indent-level: 8
575
 *  c-basic-offset: 8
576
 * End:
577
 */