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 #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 
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. */
pbuf_le32toh(uint32_t v)79 static 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. */
pbuf_le64toh(uint64_t v)86 static 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  */
protobuf_type(const char * s)94 int protobuf_type(const char *s)
95 {
96 	/* varint types. */
97 	if (strcmp(s, "int32") == 0)
98 		return PBUF_T_VARINT_INT32;
99 	else if (strcmp(s, "uint32") == 0)
100 		return PBUF_T_VARINT_UINT32;
101 	else if (strcmp(s, "sint32") == 0)
102 		return PBUF_T_VARINT_SINT32;
103 	else if (strcmp(s, "int64") == 0)
104 		return PBUF_T_VARINT_INT64;
105 	else if (strcmp(s, "uint64") == 0)
106 		return PBUF_T_VARINT_UINT64;
107 	else if (strcmp(s, "sint64") == 0)
108 		return PBUF_T_VARINT_SINT64;
109 	else if (strcmp(s, "bool") == 0)
110 		return PBUF_T_VARINT_BOOL;
111 	else if (strcmp(s, "enum") == 0)
112 		return PBUF_T_VARINT_ENUM;
113 
114 	/* 32bit fixed size types. */
115 	else if (strcmp(s, "fixed32") == 0)
116 		return PBUF_T_32BIT_FIXED32;
117 	else if (strcmp(s, "sfixed32") == 0)
118 		return PBUF_T_32BIT_SFIXED32;
119 	else if (strcmp(s, "float") == 0)
120 		return PBUF_T_32BIT_FLOAT;
121 
122 	/* 64bit fixed size types. */
123 	else if (strcmp(s, "fixed64") == 0)
124 		return PBUF_T_64BIT_FIXED64;
125 	else if (strcmp(s, "sfixed64") == 0)
126 		return PBUF_T_64BIT_SFIXED64;
127 	else if (strcmp(s, "double") == 0)
128 		return PBUF_T_64BIT_DOUBLE;
129 	else
130 		return -1;
131 }
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
protobuf_varint(uint64_t * val,unsigned char * pos,size_t len)139 protobuf_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. */
161 		if (shift > 63)
162 			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  */
174 static inline int
protobuf_decode_varint(uint64_t * val,unsigned char ** pos,size_t * len)175 protobuf_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. */
197 		if (shift > 63)
198 			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  */
210 int
protobuf_skip_varint(unsigned char ** pos,size_t * len,size_t vlen)211 protobuf_skip_varint(unsigned char **pos, size_t *len, size_t vlen)
212 {
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. */
230 		if (shift > 63)
231 			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  */
244 static inline int
protobuf_varint_getlen(unsigned char * pos,size_t len)245 protobuf_varint_getlen(unsigned char *pos, size_t len)
246 {
247 	unsigned char *spos;
248 	unsigned int shift;
249 
250 	shift = 0;
251 	spos = pos;
252 
253 	while (len > 0) {
254 		int stop = !(*pos & PBUF_VARINT_DONT_STOP_BITMASK);
255 
256 		++pos;
257 		--len;
258 
259 		if (stop)
260 			break;
261 		else if (!len)
262 			return -1;
263 
264 		shift += 7;
265 		/* The maximum length in bytes of a 64-bit encoded value is 10. */
266 		if (shift > 63)
267 			return -1;
268 	}
269 
270 	return pos - spos;
271 }
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  */
protobuf_smp_store_varint(struct sample * smp,int type,unsigned char * pos,size_t len,size_t vlen)279 int protobuf_smp_store_varint(struct sample *smp, int type,
280                               unsigned char *pos, size_t len, size_t vlen)
281 {
282 	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;
290 
291 		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:
324 		return 0;
325 
326 	}
327 
328 	return 1;
329 }
330 
331 /*
332  * Move forward <*pos> buffer by 8 bytes. Used to skip a 64bit field.
333  */
protobuf_skip_64bit(unsigned char ** pos,size_t * len,size_t vlen)334 int 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
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  */
protobuf_smp_store_64bit(struct sample * smp,int type,unsigned char * pos,size_t len,size_t vlen)351 int protobuf_smp_store_64bit(struct sample *smp, int type,
352                              unsigned char *pos, size_t len, size_t vlen)
353 {
354 	if (len < sizeof(uint64_t))
355 	    return 0;
356 
357 	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 	}
381 
382 	return 1;
383 }
384 
385 /*
386  * Move forward <*pos> buffer by <vlen> bytes. Use to skip a length-delimited
387  * field.
388  */
protobuf_skip_vlen(unsigned char ** pos,size_t * len,size_t vlen)389 int 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  */
protobuf_smp_store_vlen(struct sample * smp,int type,unsigned char * pos,size_t len,size_t vlen)405 int protobuf_smp_store_vlen(struct sample *smp, int type,
406                             unsigned char *pos, size_t len, size_t vlen)
407 {
408 	if (len < vlen)
409 		return 0;
410 
411 	if (type != PBUF_T_BINARY)
412 		return 0;
413 
414 	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  */
protobuf_skip_32bit(unsigned char ** pos,size_t * len,size_t vlen)425 int 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
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  */
protobuf_smp_store_32bit(struct sample * smp,int type,unsigned char * pos,size_t len,size_t vlen)442 int protobuf_smp_store_32bit(struct sample *smp, int type,
443                              unsigned char *pos, size_t len, size_t vlen)
444 {
445 	if (len < sizeof(uint32_t))
446 	    return 0;
447 
448 	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 	}
477 
478 	return 1;
479 }
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  */
protobuf_field_lookup(const struct arg * arg_p,struct sample * smp,unsigned char ** pos,size_t * len)487 static 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 
570 #endif /* _HAPROXY_PROTOBUF_H */
571 
572 /*
573  * Local variables:
574  *  c-indent-level: 8
575  *  c-basic-offset: 8
576  * End:
577  */
578