blob: b3f7b4f8bb73c7098f71c7f61e9c0c3c9f5433b2 [file] [log] [blame]
Christopher Faulet4ff3e572017-02-24 14:31:11 +01001/*
2 * include/proto/spoe.h
3 * Encoding/Decoding functions for the SPOE filters (and other helpers).
4 *
5 * Copyright (C) 2017 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
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_SPOE_H
23#define _PROTO_SPOE_H
24
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020025#include <common/standard.h>
26
Christopher Faulet4ff3e572017-02-24 14:31:11 +010027#include <types/spoe.h>
28
29#include <proto/sample.h>
30
Christopher Faulet4ff3e572017-02-24 14:31:11 +010031
32/* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy
33 * of <str>. It must have enough space in <*buf> to encode the buffer, else an
34 * error is triggered.
35 * On success, it returns <len> and <*buf> is moved after the encoded value. If
36 * an error occurred, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020037static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +010038spoe_encode_buffer(const char *str, size_t len, char **buf, char *end)
39{
40 char *p = *buf;
41 int ret;
42
43 if (p >= end)
44 return -1;
45
46 if (!len) {
47 *p++ = 0;
48 *buf = p;
49 return 0;
50 }
51
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020052 ret = encode_varint(len, &p, end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +010053 if (ret == -1 || p + len > end)
54 return -1;
55
56 memcpy(p, str, len);
57 *buf = p + len;
58 return len;
59}
60
61/* Encode a buffer, possibly partially. It does the same thing than
62 * 'spoe_encode_buffer', but if there is not enough space, it does not fail.
63 * On success, it returns the number of copied bytes and <*buf> is moved after
Joseph Herlantf7f60312018-11-15 13:46:49 -080064 * the encoded value. If an error occurred, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020065static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +010066spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end)
67{
68 char *p = *buf;
69 int ret;
70
71 if (p >= end)
72 return -1;
73
74 if (!len) {
75 *p++ = 0;
76 *buf = p;
77 return 0;
78 }
79
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020080 ret = encode_varint(len, &p, end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +010081 if (ret == -1 || p >= end)
82 return -1;
83
84 ret = (p+len < end) ? len : (end - p);
85 memcpy(p, str, ret);
86 *buf = p + ret;
87 return ret;
88}
89
90/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str>
91 * points on the first byte of the buffer.
92 * On success, it returns the buffer length and <*buf> is moved after the
93 * encoded buffer. Otherwise, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020094static inline int
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +020095spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len)
Christopher Faulet4ff3e572017-02-24 14:31:11 +010096{
97 char *p = *buf;
98 uint64_t sz;
99 int ret;
100
101 *str = NULL;
102 *len = 0;
103
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200104 ret = decode_varint(&p, end, &sz);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100105 if (ret == -1 || p + sz > end)
106 return -1;
107
108 *str = p;
109 *len = sz;
110 *buf = p + sz;
111 return sz;
112}
113
114/* Encode a typed data using value in <smp>. On success, it returns the number
115 * of copied bytes and <*buf> is moved after the encoded value. If an error
Joseph Herlantf7f60312018-11-15 13:46:49 -0800116 * occurred, it returns -1.
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100117 *
118 * If the value is too big to be encoded, depending on its type, then encoding
119 * failed or the value is partially encoded. Only strings and binaries can be
Christopher Faulet85db3212019-04-26 14:30:15 +0200120 * partially encoded. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200121static inline int
Christopher Faulet85db3212019-04-26 14:30:15 +0200122spoe_encode_data(struct sample *smp, char **buf, char *end)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100123{
124 char *p = *buf;
125 int ret;
126
127 if (p >= end)
128 return -1;
129
130 if (smp == NULL) {
131 *p++ = SPOE_DATA_T_NULL;
132 goto end;
133 }
134
135 switch (smp->data.type) {
136 case SMP_T_BOOL:
137 *p = SPOE_DATA_T_BOOL;
138 *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
139 break;
140
141 case SMP_T_SINT:
142 *p++ = SPOE_DATA_T_INT64;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200143 if (encode_varint(smp->data.u.sint, &p, end) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100144 return -1;
145 break;
146
147 case SMP_T_IPV4:
148 if (p + 5 > end)
149 return -1;
150 *p++ = SPOE_DATA_T_IPV4;
151 memcpy(p, &smp->data.u.ipv4, 4);
152 p += 4;
153 break;
154
155 case SMP_T_IPV6:
156 if (p + 17 > end)
157 return -1;
158 *p++ = SPOE_DATA_T_IPV6;
159 memcpy(p, &smp->data.u.ipv6, 16);
160 p += 16;
161 break;
162
163 case SMP_T_STR:
164 case SMP_T_BIN: {
Christopher Faulet85db3212019-04-26 14:30:15 +0200165 /* If defined, get length and offset of the sample by reading the sample
166 * context. ctx.a[0] is the pointer to the length and ctx.a[1] is the
167 * pointer to the offset. If the offset is greater than 0, it means the
168 * sample is partially encoded. In this case, we only need to encode the
169 * reamining. When all the sample is encoded, the offset is reset to 0.
170 * So the caller know it can try to encode the next sample. */
Willy Tarreau83061a82018-07-13 11:56:34 +0200171 struct buffer *chk = &smp->data.u.str;
Christopher Faulet3b1d0042019-05-06 09:53:10 +0200172 unsigned int *len = smp->ctx.a[0];
173 unsigned int *off = smp->ctx.a[1];
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100174
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100175 if (!*off) {
176 /* First evaluation of the sample : encode the
177 * type (string or binary), the buffer length
178 * (as a varint) and at least 1 byte of the
179 * buffer. */
Willy Tarreau83061a82018-07-13 11:56:34 +0200180 struct buffer *chk = &smp->data.u.str;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100181
182 *p++ = (smp->data.type == SMP_T_STR)
183 ? SPOE_DATA_T_STR
184 : SPOE_DATA_T_BIN;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200185 ret = spoe_encode_frag_buffer(chk->area,
186 chk->data, &p,
187 end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100188 if (ret == -1)
189 return -1;
Kevin Zhuf7f54282019-04-26 14:00:01 +0800190 *len = chk->data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100191 }
192 else {
193 /* The sample has been fragmented, encode remaining data */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800194 ret = MIN(*len - *off, end - p);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200195 memcpy(p, chk->area + *off, ret);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100196 p += ret;
197 }
198 /* Now update <*off> */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800199 if (ret + *off != *len)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100200 *off += ret;
201 else
202 *off = 0;
203 break;
204 }
205
206 case SMP_T_METH: {
207 char *m;
208 size_t len;
209
210 *p++ = SPOE_DATA_T_STR;
211 switch (smp->data.u.meth.meth) {
212 case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
213 case HTTP_METH_GET : m = "GET"; len = 3; break;
214 case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
215 case HTTP_METH_POST : m = "POST"; len = 4; break;
216 case HTTP_METH_PUT : m = "PUT"; len = 3; break;
217 case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
218 case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
219 case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
220
221 default :
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200222 m = smp->data.u.meth.str.area;
223 len = smp->data.u.meth.str.data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100224 }
225 if (spoe_encode_buffer(m, len, &p, end) == -1)
226 return -1;
227 break;
228 }
229
230 default:
231 *p++ = SPOE_DATA_T_NULL;
232 break;
233 }
234
235 end:
236 ret = (p - *buf);
237 *buf = p;
238 return ret;
239}
240
241/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
242 * of skipped bytes is returned and the <*buf> is moved after skipped data.
243 *
244 * A types data is composed of a type (1 byte) and corresponding data:
245 * - boolean: non additional data (0 bytes)
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200246 * - integers: a variable-length integer (see decode_varint)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100247 * - ipv4: 4 bytes
248 * - ipv6: 16 bytes
249 * - binary and string: a buffer prefixed by its size, a variable-length
250 * integer (see spoe_decode_buffer) */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200251static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100252spoe_skip_data(char **buf, char *end)
253{
254 char *str, *p = *buf;
255 int type, ret;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200256 uint64_t v, sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100257
258 if (p >= end)
259 return -1;
260
261 type = *p++;
262 switch (type & SPOE_DATA_T_MASK) {
263 case SPOE_DATA_T_BOOL:
264 break;
265 case SPOE_DATA_T_INT32:
266 case SPOE_DATA_T_INT64:
267 case SPOE_DATA_T_UINT32:
268 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200269 if (decode_varint(&p, end, &v) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100270 return -1;
271 break;
272 case SPOE_DATA_T_IPV4:
273 if (p+4 > end)
274 return -1;
275 p += 4;
276 break;
277 case SPOE_DATA_T_IPV6:
278 if (p+16 > end)
279 return -1;
280 p += 16;
281 break;
282 case SPOE_DATA_T_STR:
283 case SPOE_DATA_T_BIN:
284 /* All the buffer must be skipped */
285 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
286 return -1;
287 break;
288 }
289
290 ret = (p - *buf);
291 *buf = p;
292 return ret;
293}
294
295/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
296 * otherwise the number of read bytes is returned and <*buf> is moved after the
297 * decoded data. See spoe_skip_data for details. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200298static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100299spoe_decode_data(char **buf, char *end, struct sample *smp)
300{
301 char *str, *p = *buf;
302 int type, r = 0;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200303 uint64_t sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100304
305 if (p >= end)
306 return -1;
307
308 type = *p++;
309 switch (type & SPOE_DATA_T_MASK) {
310 case SPOE_DATA_T_BOOL:
311 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
312 smp->data.type = SMP_T_BOOL;
313 break;
314 case SPOE_DATA_T_INT32:
315 case SPOE_DATA_T_INT64:
316 case SPOE_DATA_T_UINT32:
317 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200318 if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100319 return -1;
320 smp->data.type = SMP_T_SINT;
321 break;
322 case SPOE_DATA_T_IPV4:
323 if (p+4 > end)
324 return -1;
325 smp->data.type = SMP_T_IPV4;
326 memcpy(&smp->data.u.ipv4, p, 4);
327 p += 4;
328 break;
329 case SPOE_DATA_T_IPV6:
330 if (p+16 > end)
331 return -1;
332 memcpy(&smp->data.u.ipv6, p, 16);
333 smp->data.type = SMP_T_IPV6;
334 p += 16;
335 break;
336 case SPOE_DATA_T_STR:
337 case SPOE_DATA_T_BIN:
338 /* All the buffer must be decoded */
339 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
340 return -1;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200341 smp->data.u.str.area = str;
342 smp->data.u.str.data = sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100343 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
344 break;
345 }
346
347 r = (p - *buf);
348 *buf = p;
349 return r;
350}
351
352#endif /* _PROTO_SPOE_H */