mirror of
				https://git.haproxy.org/git/haproxy.git/
				synced 2025-11-04 10:31:14 +01:00 
			
		
		
		
	This patch fixes a bad stop condition when decoding a protocol buffer variable integer whose maximum lenghts are 10, shifting a uint64_t value by more than 63. Thank you to Ilya for having reported this issue. Must be backported to 2.1 and 2.0.
		
			
				
	
	
		
			578 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			578 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * include/proto/protocol_buffers.h
 | 
						|
 * This file contains functions and macros declarations for protocol buffers decoding.
 | 
						|
 *
 | 
						|
 * Copyright 2012 Willy Tarreau <w@1wt.eu>
 | 
						|
 *
 | 
						|
 * This library is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation, version 2.1
 | 
						|
 * exclusively.
 | 
						|
 *
 | 
						|
 * This library is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this library; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef _PROTO_PROTOCOL_BUFFERS_H
 | 
						|
#define _PROTO_PROTOCOL_BUFFERS_H
 | 
						|
 | 
						|
#include <inttypes.h>
 | 
						|
#include <types/arg.h>
 | 
						|
#include <types/protocol_buffers.h>
 | 
						|
#include <proto/sample.h>
 | 
						|
 | 
						|
#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, int type,
 | 
						|
                              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, int type,
 | 
						|
                             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, int type,
 | 
						|
                            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, int type,
 | 
						|
                             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,
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Note that the field values with protocol buffers 32bit and 64bit fixed size as type
 | 
						|
 * are sent in little-endian byte order to the network.
 | 
						|
 */
 | 
						|
 | 
						|
/* Convert a little-endian ordered 32bit integer to the byte order of the host. */
 | 
						|
static inline uint32_t pbuf_le32toh(uint32_t v)
 | 
						|
{
 | 
						|
	uint8_t *p = (uint8_t *)&v;
 | 
						|
	return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
 | 
						|
}
 | 
						|
 | 
						|
/* Convert a little-endian ordered 64bit integer to the byte order of the host. */
 | 
						|
static inline uint64_t pbuf_le64toh(uint64_t v)
 | 
						|
{
 | 
						|
	return (uint64_t)(pbuf_le32toh(v >> 32)) << 32 | pbuf_le32toh(v);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return a protobuf type enum from <s> string if succedeed, -1 if not.
 | 
						|
 */
 | 
						|
int protobuf_type(const char *s)
 | 
						|
{
 | 
						|
	/* varint types. */
 | 
						|
	if (!strcmp(s, "int32"))
 | 
						|
		return PBUF_T_VARINT_INT32;
 | 
						|
	else if (!strcmp(s, "uint32"))
 | 
						|
		return PBUF_T_VARINT_UINT32;
 | 
						|
	else if (!strcmp(s, "sint32"))
 | 
						|
		return PBUF_T_VARINT_SINT32;
 | 
						|
	else if (!strcmp(s, "int64"))
 | 
						|
		return PBUF_T_VARINT_INT64;
 | 
						|
	else if (!strcmp(s, "uint64"))
 | 
						|
		return PBUF_T_VARINT_UINT64;
 | 
						|
	else if (!strcmp(s, "sint64"))
 | 
						|
		return PBUF_T_VARINT_SINT64;
 | 
						|
	else if (!strcmp(s, "bool"))
 | 
						|
		return PBUF_T_VARINT_BOOL;
 | 
						|
	else if (!strcmp(s, "enum"))
 | 
						|
		return PBUF_T_VARINT_ENUM;
 | 
						|
 | 
						|
	/* 32bit fixed size types. */
 | 
						|
	else if (!strcmp(s, "fixed32"))
 | 
						|
		return PBUF_T_32BIT_FIXED32;
 | 
						|
	else if (!strcmp(s, "sfixed32"))
 | 
						|
		return PBUF_T_32BIT_SFIXED32;
 | 
						|
	else if (!strcmp(s, "float"))
 | 
						|
		return PBUF_T_32BIT_FLOAT;
 | 
						|
 | 
						|
	/* 64bit fixed size types. */
 | 
						|
	else if (!strcmp(s, "fixed64"))
 | 
						|
		return PBUF_T_64BIT_FIXED64;
 | 
						|
	else if (!strcmp(s, "sfixed64"))
 | 
						|
		return PBUF_T_64BIT_SFIXED64;
 | 
						|
	else if (!strcmp(s, "double"))
 | 
						|
		return PBUF_T_64BIT_DOUBLE;
 | 
						|
	else
 | 
						|
		return -1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Decode a protocol buffers varint located in a buffer at <pos> address with
 | 
						|
 * <len> as length. The decoded value is stored at <val>.
 | 
						|
 * Returns 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
protobuf_varint(uint64_t *val, unsigned char *pos, size_t len)
 | 
						|
{
 | 
						|
	unsigned int shift;
 | 
						|
 | 
						|
	*val = 0;
 | 
						|
	shift = 0;
 | 
						|
 | 
						|
	while (len > 0) {
 | 
						|
		int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
 | 
						|
 | 
						|
		*val |= ((uint64_t)(*pos & PBUF_VARINT_DATA_BITMASK)) << shift;
 | 
						|
 | 
						|
		++pos;
 | 
						|
		--len;
 | 
						|
 | 
						|
		if (stop)
 | 
						|
			break;
 | 
						|
		else if (!len)
 | 
						|
			return 0;
 | 
						|
 | 
						|
		shift += 7;
 | 
						|
		/* The maximum length in bytes of a 64-bit encoded value is 10. */
 | 
						|
		if (shift > 63)
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Decode a protocol buffers varint located in a buffer at <pos> offset address with
 | 
						|
 * <len> as length address. Update <pos> and <len> consequently. Decrease <*len>
 | 
						|
 * by the number of decoded bytes. The decoded value is stored at <val>.
 | 
						|
 * Returns 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
protobuf_decode_varint(uint64_t *val, unsigned char **pos, size_t *len)
 | 
						|
{
 | 
						|
	unsigned int shift;
 | 
						|
 | 
						|
	*val = 0;
 | 
						|
	shift = 0;
 | 
						|
 | 
						|
	while (*len > 0) {
 | 
						|
		int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
 | 
						|
 | 
						|
		*val |= ((uint64_t)**pos & PBUF_VARINT_DATA_BITMASK) << shift;
 | 
						|
 | 
						|
		++*pos;
 | 
						|
		--*len;
 | 
						|
 | 
						|
		if (stop)
 | 
						|
			break;
 | 
						|
		else if (!*len)
 | 
						|
			return 0;
 | 
						|
 | 
						|
		shift += 7;
 | 
						|
		/* The maximum length in bytes of a 64-bit encoded value is 10. */
 | 
						|
		if (shift > 63)
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Skip a protocol buffer varint found at <pos> as position address with <len>
 | 
						|
 * as available length address. Update <*pos> to make it point to the next
 | 
						|
 * available byte. Decrease <*len> by the number of skipped bytes.
 | 
						|
 * Returns 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
int
 | 
						|
protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen)
 | 
						|
{
 | 
						|
	unsigned int shift;
 | 
						|
 | 
						|
	shift = 0;
 | 
						|
 | 
						|
	while (*len > 0) {
 | 
						|
		int stop = !(**pos & PBUF_VARINT_DONT_STOP_BITMASK);
 | 
						|
 | 
						|
		++*pos;
 | 
						|
		--*len;
 | 
						|
 | 
						|
		if (stop)
 | 
						|
			break;
 | 
						|
		else if (!*len)
 | 
						|
			return 0;
 | 
						|
 | 
						|
		shift += 7;
 | 
						|
		/* The maximum length in bytes of a 64-bit encoded value is 10. */
 | 
						|
		if (shift > 63)
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * If succeeded, return the length of a prococol buffers varint found at <pos> as
 | 
						|
 * position address, with <len> as address of the available bytes at <*pos>.
 | 
						|
 * Update <*pos> to make it point to the next available byte. Decrease <*len>
 | 
						|
 * by the number of bytes used to encode this varint.
 | 
						|
 * Return -1 if failed.
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
protobuf_varint_getlen(unsigned char *pos, size_t len)
 | 
						|
{
 | 
						|
	unsigned char *spos;
 | 
						|
	unsigned int shift;
 | 
						|
 | 
						|
	shift = 0;
 | 
						|
	spos = pos;
 | 
						|
 | 
						|
	while (len > 0) {
 | 
						|
		int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
 | 
						|
 | 
						|
		++pos;
 | 
						|
		--len;
 | 
						|
 | 
						|
		if (stop)
 | 
						|
			break;
 | 
						|
		else if (!len)
 | 
						|
			return -1;
 | 
						|
 | 
						|
		shift += 7;
 | 
						|
		/* The maximum length in bytes of a 64-bit encoded value is 10. */
 | 
						|
		if (shift > 63)
 | 
						|
			return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return pos - spos;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Store a varint field value in a sample from <pos> buffer
 | 
						|
 * with <len> available bytes after having decoded it if needed
 | 
						|
 * depending on <type> the expected protocol buffer type of the field.
 | 
						|
 * Return 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
int protobuf_smp_store_varint(struct sample *smp, int type,
 | 
						|
                              unsigned char *pos, size_t len, size_t vlen)
 | 
						|
{
 | 
						|
	switch (type) {
 | 
						|
	case PBUF_T_BINARY:
 | 
						|
	{
 | 
						|
		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;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	case PBUF_T_VARINT_INT32 ... PBUF_T_VARINT_ENUM:
 | 
						|
	{
 | 
						|
		uint64_t varint;
 | 
						|
 | 
						|
		if (!protobuf_varint(&varint, pos, len))
 | 
						|
			return 0;
 | 
						|
 | 
						|
		smp->data.u.sint = varint;
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	case PBUF_T_VARINT_SINT32 ... PBUF_T_VARINT_SINT64:
 | 
						|
	{
 | 
						|
		uint64_t varint;
 | 
						|
 | 
						|
		if (!protobuf_varint(&varint, pos, len))
 | 
						|
			return 0;
 | 
						|
 | 
						|
		/* zigzag decoding. */
 | 
						|
		smp->data.u.sint = (varint >> 1) ^ -(varint & 1);
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	default:
 | 
						|
		return 0;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	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 <pos> buffer
 | 
						|
 * with <len> available bytes after having decoded it depending on <type>
 | 
						|
 * the expected protocol buffer type of the field.
 | 
						|
 * Return 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
int protobuf_smp_store_64bit(struct sample *smp, int type,
 | 
						|
                             unsigned char *pos, size_t len, size_t vlen)
 | 
						|
{
 | 
						|
	if (len < sizeof(uint64_t))
 | 
						|
	    return 0;
 | 
						|
 | 
						|
	switch (type) {
 | 
						|
	case PBUF_T_BINARY:
 | 
						|
		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;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PBUF_T_64BIT_FIXED64:
 | 
						|
	case PBUF_T_64BIT_SFIXED64:
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		smp->data.u.sint = pbuf_le64toh(*(uint64_t *)pos);
 | 
						|
		smp->flags = SMP_F_VOL_TEST;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PBUF_T_64BIT_DOUBLE:
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		smp->data.u.sint = pbuf_le64toh(*(double *)pos);
 | 
						|
		smp->flags = SMP_F_VOL_TEST;
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Move forward <*pos> buffer by <vlen> 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 <vlen>-bytes length-delimited field value in a sample from <pos>
 | 
						|
 * buffer with <len> available bytes.
 | 
						|
 * Return 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
int protobuf_smp_store_vlen(struct sample *smp, int type,
 | 
						|
                            unsigned char *pos, size_t len, size_t vlen)
 | 
						|
{
 | 
						|
	if (len < vlen)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (type != PBUF_T_BINARY)
 | 
						|
		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 <pos> buffer
 | 
						|
 * with <len> available bytes after having decoded it depending on <type>
 | 
						|
 * the expected protocol buffer type of the field.
 | 
						|
 * Return 1 if succeeded, 0 if not.
 | 
						|
 */
 | 
						|
int protobuf_smp_store_32bit(struct sample *smp, int type,
 | 
						|
                             unsigned char *pos, size_t len, size_t vlen)
 | 
						|
{
 | 
						|
	if (len < sizeof(uint32_t))
 | 
						|
	    return 0;
 | 
						|
 | 
						|
	switch (type) {
 | 
						|
	case PBUF_T_BINARY:
 | 
						|
		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;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PBUF_T_32BIT_FIXED32:
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		smp->data.u.sint = pbuf_le32toh(*(uint32_t *)pos);
 | 
						|
		smp->flags = SMP_F_VOL_TEST;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PBUF_T_32BIT_SFIXED32:
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		smp->data.u.sint = (int32_t)pbuf_le32toh(*(uint32_t *)pos);
 | 
						|
		smp->flags = SMP_F_VOL_TEST;
 | 
						|
		break;
 | 
						|
 | 
						|
	case PBUF_T_32BIT_FLOAT:
 | 
						|
		smp->data.type = SMP_T_SINT;
 | 
						|
		smp->data.u.sint = pbuf_le32toh(*(float *)pos);
 | 
						|
		smp->flags = SMP_F_VOL_TEST;
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Lookup for a protocol buffers field whose parameters are provided by <arg_p>
 | 
						|
 * first argument in the buffer with <pos> as address and <len> as length address.
 | 
						|
 * If found, store its value depending on the type of storage to use provided by <arg_p>
 | 
						|
 * second argument and return 1, 0 if not.
 | 
						|
 */
 | 
						|
static inline int protobuf_field_lookup(const struct arg *arg_p, struct sample *smp,
 | 
						|
                                        unsigned char **pos, size_t *len)
 | 
						|
{
 | 
						|
	unsigned int *fid;
 | 
						|
	size_t fid_sz;
 | 
						|
	int type;
 | 
						|
	uint64_t elen;
 | 
						|
	int field;
 | 
						|
 | 
						|
	fid = arg_p[0].data.fid.ids;
 | 
						|
	fid_sz = arg_p[0].data.fid.sz;
 | 
						|
	type = arg_p[1].data.sint;
 | 
						|
 | 
						|
	/* Length of the length-delimited messages if any. */
 | 
						|
	elen = 0;
 | 
						|
	field = 0;
 | 
						|
 | 
						|
	while (field < fid_sz) {
 | 
						|
		int found;
 | 
						|
		uint64_t key, sleft;
 | 
						|
		struct protobuf_parser_def *pbuf_parser = NULL;
 | 
						|
		unsigned int wire_type, field_number;
 | 
						|
 | 
						|
		if ((ssize_t)*len <= 0)
 | 
						|
			return 0;
 | 
						|
 | 
						|
		/* Remaining bytes saving. */
 | 
						|
		sleft = *len;
 | 
						|
 | 
						|
		/* Key decoding */
 | 
						|
		if (!protobuf_decode_varint(&key, pos, len))
 | 
						|
			return 0;
 | 
						|
 | 
						|
		wire_type = key & 0x7;
 | 
						|
		field_number = key >> 3;
 | 
						|
		found = field_number == fid[field];
 | 
						|
 | 
						|
		/* Skip the data if the current field does not match. */
 | 
						|
		switch (wire_type) {
 | 
						|
		case PBUF_TYPE_VARINT:
 | 
						|
		case PBUF_TYPE_32BIT:
 | 
						|
		case PBUF_TYPE_64BIT:
 | 
						|
			pbuf_parser = &protobuf_parser_defs[wire_type];
 | 
						|
			if (!found && !pbuf_parser->skip(pos, len, 0))
 | 
						|
				return 0;
 | 
						|
			break;
 | 
						|
 | 
						|
		case PBUF_TYPE_LENGTH_DELIMITED:
 | 
						|
			/* Decode the length of this length-delimited field. */
 | 
						|
			if (!protobuf_decode_varint(&elen, pos, len) || elen > *len)
 | 
						|
				return 0;
 | 
						|
 | 
						|
			/* The size of the current field is computed from here to skip
 | 
						|
			 * the bytes used to encode the previous length.*
 | 
						|
			 */
 | 
						|
			sleft = *len;
 | 
						|
			pbuf_parser = &protobuf_parser_defs[wire_type];
 | 
						|
			if (!found && !pbuf_parser->skip(pos, len, elen))
 | 
						|
				return 0;
 | 
						|
			break;
 | 
						|
 | 
						|
		default:
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Store the data if found. Note that <pbuf_parser> is not NULL */
 | 
						|
		if (found && field == fid_sz - 1)
 | 
						|
			return pbuf_parser->smp_store(smp, type, *pos, *len, elen);
 | 
						|
 | 
						|
		if ((ssize_t)(elen) > 0)
 | 
						|
			elen -= sleft - *len;
 | 
						|
 | 
						|
		if (found) {
 | 
						|
			field++;
 | 
						|
		}
 | 
						|
		else if ((ssize_t)elen <= 0) {
 | 
						|
			field = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* _PROTO_PROTOCOL_BUFFERS_H */
 | 
						|
 | 
						|
/*
 | 
						|
 * Local variables:
 | 
						|
 *  c-indent-level: 8
 | 
						|
 *  c-basic-offset: 8
 | 
						|
 * End:
 | 
						|
 */
 |