blob: d9e90982016249f8ce8494b60271c9097a955e35 [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
Willy Tarreau889faf42020-06-01 12:09:26 +020025#include <haproxy/intops.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020026#include <haproxy/sample-t.h>
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020027
Christopher Faulet4ff3e572017-02-24 14:31:11 +010028#include <types/spoe.h>
29
Christopher Faulet4ff3e572017-02-24 14:31:11 +010030
31/* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy
32 * of <str>. It must have enough space in <*buf> to encode the buffer, else an
33 * error is triggered.
34 * On success, it returns <len> and <*buf> is moved after the encoded value. If
35 * an error occurred, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020036static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +010037spoe_encode_buffer(const char *str, size_t len, char **buf, char *end)
38{
39 char *p = *buf;
40 int ret;
41
42 if (p >= end)
43 return -1;
44
45 if (!len) {
46 *p++ = 0;
47 *buf = p;
48 return 0;
49 }
50
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020051 ret = encode_varint(len, &p, end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +010052 if (ret == -1 || p + len > end)
53 return -1;
54
55 memcpy(p, str, len);
56 *buf = p + len;
57 return len;
58}
59
60/* Encode a buffer, possibly partially. It does the same thing than
61 * 'spoe_encode_buffer', but if there is not enough space, it does not fail.
62 * On success, it returns the number of copied bytes and <*buf> is moved after
Joseph Herlantf7f60312018-11-15 13:46:49 -080063 * the encoded value. If an error occurred, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020064static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +010065spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end)
66{
67 char *p = *buf;
68 int ret;
69
70 if (p >= end)
71 return -1;
72
73 if (!len) {
74 *p++ = 0;
75 *buf = p;
76 return 0;
77 }
78
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +020079 ret = encode_varint(len, &p, end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +010080 if (ret == -1 || p >= end)
81 return -1;
82
83 ret = (p+len < end) ? len : (end - p);
84 memcpy(p, str, ret);
85 *buf = p + ret;
86 return ret;
87}
88
89/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str>
90 * points on the first byte of the buffer.
91 * On success, it returns the buffer length and <*buf> is moved after the
92 * encoded buffer. Otherwise, it returns -1. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +020093static inline int
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +020094spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len)
Christopher Faulet4ff3e572017-02-24 14:31:11 +010095{
96 char *p = *buf;
97 uint64_t sz;
98 int ret;
99
100 *str = NULL;
101 *len = 0;
102
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200103 ret = decode_varint(&p, end, &sz);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100104 if (ret == -1 || p + sz > end)
105 return -1;
106
107 *str = p;
108 *len = sz;
109 *buf = p + sz;
110 return sz;
111}
112
113/* Encode a typed data using value in <smp>. On success, it returns the number
114 * of copied bytes and <*buf> is moved after the encoded value. If an error
Joseph Herlantf7f60312018-11-15 13:46:49 -0800115 * occurred, it returns -1.
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100116 *
117 * If the value is too big to be encoded, depending on its type, then encoding
118 * failed or the value is partially encoded. Only strings and binaries can be
Christopher Faulet85db3212019-04-26 14:30:15 +0200119 * partially encoded. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200120static inline int
Christopher Faulet85db3212019-04-26 14:30:15 +0200121spoe_encode_data(struct sample *smp, char **buf, char *end)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100122{
123 char *p = *buf;
124 int ret;
125
126 if (p >= end)
127 return -1;
128
129 if (smp == NULL) {
130 *p++ = SPOE_DATA_T_NULL;
131 goto end;
132 }
133
134 switch (smp->data.type) {
135 case SMP_T_BOOL:
136 *p = SPOE_DATA_T_BOOL;
137 *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
138 break;
139
140 case SMP_T_SINT:
141 *p++ = SPOE_DATA_T_INT64;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200142 if (encode_varint(smp->data.u.sint, &p, end) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100143 return -1;
144 break;
145
146 case SMP_T_IPV4:
147 if (p + 5 > end)
148 return -1;
149 *p++ = SPOE_DATA_T_IPV4;
150 memcpy(p, &smp->data.u.ipv4, 4);
151 p += 4;
152 break;
153
154 case SMP_T_IPV6:
155 if (p + 17 > end)
156 return -1;
157 *p++ = SPOE_DATA_T_IPV6;
158 memcpy(p, &smp->data.u.ipv6, 16);
159 p += 16;
160 break;
161
162 case SMP_T_STR:
163 case SMP_T_BIN: {
Christopher Faulet85db3212019-04-26 14:30:15 +0200164 /* If defined, get length and offset of the sample by reading the sample
165 * context. ctx.a[0] is the pointer to the length and ctx.a[1] is the
166 * pointer to the offset. If the offset is greater than 0, it means the
167 * sample is partially encoded. In this case, we only need to encode the
168 * reamining. When all the sample is encoded, the offset is reset to 0.
169 * So the caller know it can try to encode the next sample. */
Willy Tarreau83061a82018-07-13 11:56:34 +0200170 struct buffer *chk = &smp->data.u.str;
Christopher Faulet3b1d0042019-05-06 09:53:10 +0200171 unsigned int *len = smp->ctx.a[0];
172 unsigned int *off = smp->ctx.a[1];
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100173
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100174 if (!*off) {
175 /* First evaluation of the sample : encode the
176 * type (string or binary), the buffer length
177 * (as a varint) and at least 1 byte of the
178 * buffer. */
Willy Tarreau83061a82018-07-13 11:56:34 +0200179 struct buffer *chk = &smp->data.u.str;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100180
181 *p++ = (smp->data.type == SMP_T_STR)
182 ? SPOE_DATA_T_STR
183 : SPOE_DATA_T_BIN;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200184 ret = spoe_encode_frag_buffer(chk->area,
185 chk->data, &p,
186 end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100187 if (ret == -1)
188 return -1;
Kevin Zhuf7f54282019-04-26 14:00:01 +0800189 *len = chk->data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100190 }
191 else {
192 /* The sample has been fragmented, encode remaining data */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800193 ret = MIN(*len - *off, end - p);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200194 memcpy(p, chk->area + *off, ret);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100195 p += ret;
196 }
197 /* Now update <*off> */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800198 if (ret + *off != *len)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100199 *off += ret;
200 else
201 *off = 0;
202 break;
203 }
204
205 case SMP_T_METH: {
206 char *m;
207 size_t len;
208
209 *p++ = SPOE_DATA_T_STR;
210 switch (smp->data.u.meth.meth) {
211 case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
212 case HTTP_METH_GET : m = "GET"; len = 3; break;
213 case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
214 case HTTP_METH_POST : m = "POST"; len = 4; break;
215 case HTTP_METH_PUT : m = "PUT"; len = 3; break;
216 case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
217 case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
218 case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
219
220 default :
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200221 m = smp->data.u.meth.str.area;
222 len = smp->data.u.meth.str.data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100223 }
224 if (spoe_encode_buffer(m, len, &p, end) == -1)
225 return -1;
226 break;
227 }
228
229 default:
230 *p++ = SPOE_DATA_T_NULL;
231 break;
232 }
233
234 end:
235 ret = (p - *buf);
236 *buf = p;
237 return ret;
238}
239
240/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
241 * of skipped bytes is returned and the <*buf> is moved after skipped data.
242 *
243 * A types data is composed of a type (1 byte) and corresponding data:
244 * - boolean: non additional data (0 bytes)
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200245 * - integers: a variable-length integer (see decode_varint)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100246 * - ipv4: 4 bytes
247 * - ipv6: 16 bytes
248 * - binary and string: a buffer prefixed by its size, a variable-length
249 * integer (see spoe_decode_buffer) */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200250static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100251spoe_skip_data(char **buf, char *end)
252{
253 char *str, *p = *buf;
254 int type, ret;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200255 uint64_t v, sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100256
257 if (p >= end)
258 return -1;
259
260 type = *p++;
261 switch (type & SPOE_DATA_T_MASK) {
262 case SPOE_DATA_T_BOOL:
263 break;
264 case SPOE_DATA_T_INT32:
265 case SPOE_DATA_T_INT64:
266 case SPOE_DATA_T_UINT32:
267 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200268 if (decode_varint(&p, end, &v) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100269 return -1;
270 break;
271 case SPOE_DATA_T_IPV4:
272 if (p+4 > end)
273 return -1;
274 p += 4;
275 break;
276 case SPOE_DATA_T_IPV6:
277 if (p+16 > end)
278 return -1;
279 p += 16;
280 break;
281 case SPOE_DATA_T_STR:
282 case SPOE_DATA_T_BIN:
283 /* All the buffer must be skipped */
284 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
285 return -1;
286 break;
287 }
288
289 ret = (p - *buf);
290 *buf = p;
291 return ret;
292}
293
294/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
295 * otherwise the number of read bytes is returned and <*buf> is moved after the
296 * decoded data. See spoe_skip_data for details. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200297static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100298spoe_decode_data(char **buf, char *end, struct sample *smp)
299{
300 char *str, *p = *buf;
301 int type, r = 0;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200302 uint64_t sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100303
304 if (p >= end)
305 return -1;
306
307 type = *p++;
308 switch (type & SPOE_DATA_T_MASK) {
309 case SPOE_DATA_T_BOOL:
310 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
311 smp->data.type = SMP_T_BOOL;
312 break;
313 case SPOE_DATA_T_INT32:
314 case SPOE_DATA_T_INT64:
315 case SPOE_DATA_T_UINT32:
316 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200317 if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100318 return -1;
319 smp->data.type = SMP_T_SINT;
320 break;
321 case SPOE_DATA_T_IPV4:
322 if (p+4 > end)
323 return -1;
324 smp->data.type = SMP_T_IPV4;
325 memcpy(&smp->data.u.ipv4, p, 4);
326 p += 4;
327 break;
328 case SPOE_DATA_T_IPV6:
329 if (p+16 > end)
330 return -1;
331 memcpy(&smp->data.u.ipv6, p, 16);
332 smp->data.type = SMP_T_IPV6;
333 p += 16;
334 break;
335 case SPOE_DATA_T_STR:
336 case SPOE_DATA_T_BIN:
337 /* All the buffer must be decoded */
338 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
339 return -1;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200340 smp->data.u.str.area = str;
341 smp->data.u.str.data = sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100342 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
343 break;
344 }
345
346 r = (p - *buf);
347 *buf = p;
348 return r;
349}
350
351#endif /* _PROTO_SPOE_H */