blob: 86328adef1c4c134fc0493559bcef2004676542a [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
120 * partially encoded. In this case, the offset <*off> is updated to known how
121 * many bytes has been encoded. If <*off> is zero at the end, it means that all
122 * data has been encoded. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200123static inline int
Kevin Zhuf7f54282019-04-26 14:00:01 +0800124spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char **buf, char *end)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100125{
126 char *p = *buf;
127 int ret;
128
129 if (p >= end)
130 return -1;
131
132 if (smp == NULL) {
133 *p++ = SPOE_DATA_T_NULL;
134 goto end;
135 }
136
137 switch (smp->data.type) {
138 case SMP_T_BOOL:
139 *p = SPOE_DATA_T_BOOL;
140 *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
141 break;
142
143 case SMP_T_SINT:
144 *p++ = SPOE_DATA_T_INT64;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200145 if (encode_varint(smp->data.u.sint, &p, end) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100146 return -1;
147 break;
148
149 case SMP_T_IPV4:
150 if (p + 5 > end)
151 return -1;
152 *p++ = SPOE_DATA_T_IPV4;
153 memcpy(p, &smp->data.u.ipv4, 4);
154 p += 4;
155 break;
156
157 case SMP_T_IPV6:
158 if (p + 17 > end)
159 return -1;
160 *p++ = SPOE_DATA_T_IPV6;
161 memcpy(p, &smp->data.u.ipv6, 16);
162 p += 16;
163 break;
164
165 case SMP_T_STR:
166 case SMP_T_BIN: {
Willy Tarreau83061a82018-07-13 11:56:34 +0200167 struct buffer *chk = &smp->data.u.str;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100168
169 /* Here, we need to know if the sample has already been
170 * partially encoded. If yes, we only need to encode the
171 * remaining, <*off> reprensenting the number of bytes
172 * already encoded. */
173 if (!*off) {
174 /* First evaluation of the sample : encode the
175 * type (string or binary), the buffer length
176 * (as a varint) and at least 1 byte of the
177 * buffer. */
Willy Tarreau83061a82018-07-13 11:56:34 +0200178 struct buffer *chk = &smp->data.u.str;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100179
180 *p++ = (smp->data.type == SMP_T_STR)
181 ? SPOE_DATA_T_STR
182 : SPOE_DATA_T_BIN;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200183 ret = spoe_encode_frag_buffer(chk->area,
184 chk->data, &p,
185 end);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100186 if (ret == -1)
187 return -1;
Kevin Zhuf7f54282019-04-26 14:00:01 +0800188 *len = chk->data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100189 }
190 else {
191 /* The sample has been fragmented, encode remaining data */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800192 ret = MIN(*len - *off, end - p);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200193 memcpy(p, chk->area + *off, ret);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100194 p += ret;
195 }
196 /* Now update <*off> */
Kevin Zhuf7f54282019-04-26 14:00:01 +0800197 if (ret + *off != *len)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100198 *off += ret;
199 else
200 *off = 0;
201 break;
202 }
203
204 case SMP_T_METH: {
205 char *m;
206 size_t len;
207
208 *p++ = SPOE_DATA_T_STR;
209 switch (smp->data.u.meth.meth) {
210 case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
211 case HTTP_METH_GET : m = "GET"; len = 3; break;
212 case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
213 case HTTP_METH_POST : m = "POST"; len = 4; break;
214 case HTTP_METH_PUT : m = "PUT"; len = 3; break;
215 case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
216 case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
217 case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
218
219 default :
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200220 m = smp->data.u.meth.str.area;
221 len = smp->data.u.meth.str.data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100222 }
223 if (spoe_encode_buffer(m, len, &p, end) == -1)
224 return -1;
225 break;
226 }
227
228 default:
229 *p++ = SPOE_DATA_T_NULL;
230 break;
231 }
232
233 end:
234 ret = (p - *buf);
235 *buf = p;
236 return ret;
237}
238
239/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
240 * of skipped bytes is returned and the <*buf> is moved after skipped data.
241 *
242 * A types data is composed of a type (1 byte) and corresponding data:
243 * - boolean: non additional data (0 bytes)
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200244 * - integers: a variable-length integer (see decode_varint)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100245 * - ipv4: 4 bytes
246 * - ipv6: 16 bytes
247 * - binary and string: a buffer prefixed by its size, a variable-length
248 * integer (see spoe_decode_buffer) */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200249static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100250spoe_skip_data(char **buf, char *end)
251{
252 char *str, *p = *buf;
253 int type, ret;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200254 uint64_t v, sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100255
256 if (p >= end)
257 return -1;
258
259 type = *p++;
260 switch (type & SPOE_DATA_T_MASK) {
261 case SPOE_DATA_T_BOOL:
262 break;
263 case SPOE_DATA_T_INT32:
264 case SPOE_DATA_T_INT64:
265 case SPOE_DATA_T_UINT32:
266 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200267 if (decode_varint(&p, end, &v) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100268 return -1;
269 break;
270 case SPOE_DATA_T_IPV4:
271 if (p+4 > end)
272 return -1;
273 p += 4;
274 break;
275 case SPOE_DATA_T_IPV6:
276 if (p+16 > end)
277 return -1;
278 p += 16;
279 break;
280 case SPOE_DATA_T_STR:
281 case SPOE_DATA_T_BIN:
282 /* All the buffer must be skipped */
283 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
284 return -1;
285 break;
286 }
287
288 ret = (p - *buf);
289 *buf = p;
290 return ret;
291}
292
293/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
294 * otherwise the number of read bytes is returned and <*buf> is moved after the
295 * decoded data. See spoe_skip_data for details. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200296static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100297spoe_decode_data(char **buf, char *end, struct sample *smp)
298{
299 char *str, *p = *buf;
300 int type, r = 0;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200301 uint64_t sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100302
303 if (p >= end)
304 return -1;
305
306 type = *p++;
307 switch (type & SPOE_DATA_T_MASK) {
308 case SPOE_DATA_T_BOOL:
309 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
310 smp->data.type = SMP_T_BOOL;
311 break;
312 case SPOE_DATA_T_INT32:
313 case SPOE_DATA_T_INT64:
314 case SPOE_DATA_T_UINT32:
315 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200316 if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100317 return -1;
318 smp->data.type = SMP_T_SINT;
319 break;
320 case SPOE_DATA_T_IPV4:
321 if (p+4 > end)
322 return -1;
323 smp->data.type = SMP_T_IPV4;
324 memcpy(&smp->data.u.ipv4, p, 4);
325 p += 4;
326 break;
327 case SPOE_DATA_T_IPV6:
328 if (p+16 > end)
329 return -1;
330 memcpy(&smp->data.u.ipv6, p, 16);
331 smp->data.type = SMP_T_IPV6;
332 p += 16;
333 break;
334 case SPOE_DATA_T_STR:
335 case SPOE_DATA_T_BIN:
336 /* All the buffer must be decoded */
337 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
338 return -1;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200339 smp->data.u.str.area = str;
340 smp->data.u.str.data = sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100341 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
342 break;
343 }
344
345 r = (p - *buf);
346 *buf = p;
347 return r;
348}
349
350#endif /* _PROTO_SPOE_H */