diff --git a/include/proto/protocol_buffers.h b/include/proto/protocol_buffers.h index d210a7250..58378a966 100644 --- a/include/proto/protocol_buffers.h +++ b/include/proto/protocol_buffers.h @@ -22,19 +22,53 @@ #ifndef _PROTO_PROTOCOL_BUFFERS_H #define _PROTO_PROTOCOL_BUFFERS_H +#include #include - -#define PBUF_TYPE_VARINT 0 -#define PBUF_TYPE_64BIT 1 -#define PBUF_TYPE_LENGTH_DELIMITED 2 -#define PBUF_TYPE_START_GROUP 3 -#define PBUF_TYPE_STOP_GROUP 4 -#define PBUF_TYPE_32BIT 5 +#include #define PBUF_VARINT_DONT_STOP_BIT 7 #define PBUF_VARINT_DONT_STOP_BITMASK (1 << PBUF_VARINT_DONT_STOP_BIT) #define PBUF_VARINT_DATA_BITMASK ~PBUF_VARINT_DONT_STOP_BITMASK +/* .skip and .smp_store prototypes. */ +int protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen); +int protobuf_smp_store_varint(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen); +int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen); +int protobuf_smp_store_64bit(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen); +int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen); +int protobuf_smp_store_vlen(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen); +int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen); +int protobuf_smp_store_32bit(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen); + +struct protobuf_parser_def protobuf_parser_defs [] = { + [PBUF_TYPE_VARINT ] = { + .skip = protobuf_skip_varint, + .smp_store = protobuf_smp_store_varint, + }, + [PBUF_TYPE_64BIT ] = { + .skip = protobuf_skip_64bit, + .smp_store = protobuf_smp_store_64bit, + }, + [PBUF_TYPE_LENGTH_DELIMITED] = { + .skip = protobuf_skip_vlen, + .smp_store = protobuf_smp_store_vlen, + }, + [PBUF_TYPE_START_GROUP ] = { + /* XXX Deprecated XXX */ + }, + [PBUF_TYPE_STOP_GROUP ] = { + /* XXX Deprecated XXX */ + }, + [PBUF_TYPE_32BIT ] = { + .skip = protobuf_skip_32bit, + .smp_store = protobuf_smp_store_32bit, + }, +}; + /* * Decode a protocol buffers varint located in a buffer at address with * as length. The decoded value is stored at . @@ -112,8 +146,8 @@ protobuf_decode_varint(uint64_t *val, unsigned char **pos, size_t *len) * available byte. Decrease <*len> by the number of skipped bytes. * Returns 1 if succeeded, 0 if not. */ -static inline int -protobuf_skip_varint(unsigned char **pos, size_t *len) +int +protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen) { unsigned int shift; @@ -147,23 +181,23 @@ protobuf_skip_varint(unsigned char **pos, size_t *len) * Return -1 if failed. */ static inline int -protobuf_varint_getlen(unsigned char **pos, size_t *len) +protobuf_varint_getlen(unsigned char *pos, size_t len) { unsigned char *spos; unsigned int shift; shift = 0; - spos = *pos; + spos = pos; - while (*len > 0) { - int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK); + while (len > 0) { + int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK); - ++*pos; - --*len; + ++pos; + --len; if (stop) break; - else if (!*len) + else if (!len) return -1; shift += 7; @@ -172,7 +206,129 @@ protobuf_varint_getlen(unsigned char **pos, size_t *len) return -1; } - return *pos - spos; + return pos - spos; +} + +/* + * Store a raw varint field value in a sample from buffer + * with available bytes. + * Return 1 if succeeded, 0 if not. + */ +int protobuf_smp_store_varint(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen) +{ + int varint_len; + + varint_len = protobuf_varint_getlen(pos, len); + if (varint_len == -1) + return 0; + + smp->data.type = SMP_T_BIN; + smp->data.u.str.area = (char *)pos; + smp->data.u.str.data = varint_len; + smp->flags = SMP_F_VOL_TEST; + + return 1; +} + +/* + * Move forward <*pos> buffer by 8 bytes. Used to skip a 64bit field. + */ +int protobuf_skip_64bit(unsigned char **pos, size_t *len, size_t vlen) +{ + if (*len < sizeof(uint64_t)) + return 0; + + *pos += sizeof(uint64_t); + *len -= sizeof(uint64_t); + + return 1; +} + +/* + * Store a fixed size 64bit field value in a sample from buffer + * with available bytes. + * Return 1 if succeeded, 0 if not. + */ +int protobuf_smp_store_64bit(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen) +{ + if (len < sizeof(uint64_t)) + return 0; + + smp->data.type = SMP_T_BIN; + smp->data.u.str.area = (char *)pos; + smp->data.u.str.data = sizeof(uint64_t); + smp->flags = SMP_F_VOL_TEST; + + return 1; +} + +/* + * Move forward <*pos> buffer by bytes. Use to skip a length-delimited + * field. + */ +int protobuf_skip_vlen(unsigned char **pos, size_t *len, size_t vlen) +{ + if (*len < vlen) + return 0; + + *pos += vlen; + *len -= vlen; + + return 1; +} + +/* + * Store a -bytes length-delimited field value in a sample from + * buffer with available bytes. + * Return 1 if succeeded, 0 if not. + */ +int protobuf_smp_store_vlen(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen) +{ + if (len < vlen) + return 0; + + smp->data.type = SMP_T_BIN; + smp->data.u.str.area = (char *)pos; + smp->data.u.str.data = vlen; + smp->flags = SMP_F_VOL_TEST; + + return 1; +} + +/* + * Move forward <*pos> buffer by 4 bytes. Used to skip a 32bit field. + */ +int protobuf_skip_32bit(unsigned char **pos, size_t *len, size_t vlen) +{ + if (*len < sizeof(uint32_t)) + return 0; + + *pos += sizeof(uint32_t); + *len -= sizeof(uint32_t); + + return 1; +} + +/* + * Store a fixed size 32bit field value in a sample from buffer + * with available bytes. + * Return 1 if succeeded, 0 if not. + */ +int protobuf_smp_store_32bit(struct sample *smp, + unsigned char *pos, size_t len, size_t vlen) +{ + if (len < sizeof(uint32_t)) + return 0; + + smp->data.type = SMP_T_BIN; + smp->data.u.str.area = (char *)pos; + smp->data.u.str.data = sizeof(uint32_t); + smp->flags = SMP_F_VOL_TEST; + + return 1; } #endif /* _PROTO_PROTOCOL_BUFFERS_H */ diff --git a/include/types/protocol_buffers.h b/include/types/protocol_buffers.h index af5d26214..9b067f277 100644 --- a/include/types/protocol_buffers.h +++ b/include/types/protocol_buffers.h @@ -22,11 +22,25 @@ #ifndef _TYPES_PROTOCOL_BUFFERS_H #define _TYPES_PROTOCOL_BUFFERS_H +enum protobuf_wire_type { + PBUF_TYPE_VARINT, + PBUF_TYPE_64BIT, + PBUF_TYPE_LENGTH_DELIMITED, + PBUF_TYPE_START_GROUP, /* Deprecated */ + PBUF_TYPE_STOP_GROUP, /* Deprecated */ + PBUF_TYPE_32BIT, +}; + struct pbuf_fid { unsigned int *ids; size_t sz; }; +struct protobuf_parser_def { + int (*skip)(unsigned char **pos, size_t *left, size_t vlen); + int (*smp_store)(struct sample *, unsigned char *pos, size_t left, size_t vlen); +}; + #endif /* _TYPES_PROTOCOL_BUFFERS_H */ /* diff --git a/src/sample.c b/src/sample.c index 9c20469c5..7cd1425bb 100644 --- a/src/sample.c +++ b/src/sample.c @@ -2803,8 +2803,8 @@ static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void /* Remaining bytes in the body to be parsed. */ grpc_left = smp->data.u.str.data; - while (grpc_left > GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ) { - int next_field, found; + while (grpc_left > GRPC_MSG_HEADER_SZ) { + int field, found; size_t grpc_msg_len, left; unsigned int wire_type, field_number; uint64_t key, elen; @@ -2824,8 +2824,8 @@ static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void /* Message decoding: there may be serveral key+value protobuf pairs by * gRPC message. */ - next_field = 0; - while (next_field < fid_sz) { + field = 0; + while (field < fid_sz) { uint64_t sleft; if ((ssize_t)left <= 0) @@ -2840,50 +2840,32 @@ static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void wire_type = key & 0x7; field_number = key >> 3; - found = field_number == fid[next_field]; - - if (found && field_number != fid[next_field]) - found = 0; + found = field_number == fid[field]; switch (wire_type) { case PBUF_TYPE_VARINT: - { - if (!found) { - protobuf_skip_varint(&pos, &left); - } else if (next_field == fid_sz - 1) { - int varint_len; - unsigned char *spos = pos; - - varint_len = protobuf_varint_getlen(&pos, &left); - if (varint_len == -1) - return 0; - - smp->data.type = SMP_T_BIN; - smp->data.u.str.area = (char *)spos; - smp->data.u.str.data = varint_len; - smp->flags = SMP_F_VOL_TEST; - return 1; - } - break; - } - + case PBUF_TYPE_32BIT: case PBUF_TYPE_64BIT: { + struct protobuf_parser_def *pbuf_parser; + + pbuf_parser = &protobuf_parser_defs[wire_type]; if (!found) { - pos += sizeof(uint64_t); - left -= sizeof(uint64_t); - } else if (next_field == fid_sz - 1) { - smp->data.type = SMP_T_BIN; - smp->data.u.str.area = (char *)pos; - smp->data.u.str.data = sizeof(uint64_t); - smp->flags = SMP_F_VOL_TEST; - return 1; + /* Skip the data. */ + if (!pbuf_parser->skip(&pos, &left, 0)) + return 0; } + else if (field == fid_sz - 1) { + return pbuf_parser->smp_store(smp, pos, left, 0); + } + break; } case PBUF_TYPE_LENGTH_DELIMITED: { + struct protobuf_parser_def *pbuf_parser; + /* Decode the length of this length-delimited field. */ if (!protobuf_decode_varint(&elen, &pos, &left)) return 0; @@ -2895,33 +2877,15 @@ static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void * the bytes to encode the previous lenght.* */ sleft = left; + pbuf_parser = &protobuf_parser_defs[wire_type]; if (!found) { - /* Skip the current length-delimited field. */ - pos += elen; - left -= elen; - break; - } else if (next_field == fid_sz - 1) { - smp->data.type = SMP_T_BIN; - smp->data.u.str.area = (char *)pos; - smp->data.u.str.data = elen; - smp->flags = SMP_F_VOL_TEST; - return 1; + /* Skip the data. */ + if (!pbuf_parser->skip(&pos, &left, elen)) + return 0; + } else if (field == fid_sz - 1) { + return pbuf_parser->smp_store(smp, pos, left, elen); } - break; - } - case PBUF_TYPE_32BIT: - { - if (!found) { - pos += sizeof(uint32_t); - left -= sizeof(uint32_t); - } else if (next_field == fid_sz - 1) { - smp->data.type = SMP_T_BIN; - smp->data.u.str.area = (char *)pos; - smp->data.u.str.data = sizeof(uint32_t); - smp->flags = SMP_F_VOL_TEST; - return 1; - } break; } @@ -2933,10 +2897,10 @@ static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void elen -= sleft - left; if (found) { - next_field++; + field++; } else if ((ssize_t)elen <= 0) { - next_field = 0; + field = 0; } } grpc_left -= grpc_msg_len;