blob: b6c4ee2cc37c19ace14868e52515275d0debb6f4 [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
64 * the encoded value. If an error occured, 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
116 * occured, it returns -1.
117 *
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
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100124spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end)
125{
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;
188 }
189 else {
190 /* The sample has been fragmented, encode remaining data */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200191 ret = MIN(chk->data - *off, end - p);
192 memcpy(p, chk->area + *off, ret);
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100193 p += ret;
194 }
195 /* Now update <*off> */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200196 if (ret + *off != chk->data)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100197 *off += ret;
198 else
199 *off = 0;
200 break;
201 }
202
203 case SMP_T_METH: {
204 char *m;
205 size_t len;
206
207 *p++ = SPOE_DATA_T_STR;
208 switch (smp->data.u.meth.meth) {
209 case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
210 case HTTP_METH_GET : m = "GET"; len = 3; break;
211 case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
212 case HTTP_METH_POST : m = "POST"; len = 4; break;
213 case HTTP_METH_PUT : m = "PUT"; len = 3; break;
214 case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
215 case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
216 case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
217
218 default :
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200219 m = smp->data.u.meth.str.area;
220 len = smp->data.u.meth.str.data;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100221 }
222 if (spoe_encode_buffer(m, len, &p, end) == -1)
223 return -1;
224 break;
225 }
226
227 default:
228 *p++ = SPOE_DATA_T_NULL;
229 break;
230 }
231
232 end:
233 ret = (p - *buf);
234 *buf = p;
235 return ret;
236}
237
238/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
239 * of skipped bytes is returned and the <*buf> is moved after skipped data.
240 *
241 * A types data is composed of a type (1 byte) and corresponding data:
242 * - boolean: non additional data (0 bytes)
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200243 * - integers: a variable-length integer (see decode_varint)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100244 * - ipv4: 4 bytes
245 * - ipv6: 16 bytes
246 * - binary and string: a buffer prefixed by its size, a variable-length
247 * integer (see spoe_decode_buffer) */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200248static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100249spoe_skip_data(char **buf, char *end)
250{
251 char *str, *p = *buf;
252 int type, ret;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200253 uint64_t v, sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100254
255 if (p >= end)
256 return -1;
257
258 type = *p++;
259 switch (type & SPOE_DATA_T_MASK) {
260 case SPOE_DATA_T_BOOL:
261 break;
262 case SPOE_DATA_T_INT32:
263 case SPOE_DATA_T_INT64:
264 case SPOE_DATA_T_UINT32:
265 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200266 if (decode_varint(&p, end, &v) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100267 return -1;
268 break;
269 case SPOE_DATA_T_IPV4:
270 if (p+4 > end)
271 return -1;
272 p += 4;
273 break;
274 case SPOE_DATA_T_IPV6:
275 if (p+16 > end)
276 return -1;
277 p += 16;
278 break;
279 case SPOE_DATA_T_STR:
280 case SPOE_DATA_T_BIN:
281 /* All the buffer must be skipped */
282 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
283 return -1;
284 break;
285 }
286
287 ret = (p - *buf);
288 *buf = p;
289 return ret;
290}
291
292/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
293 * otherwise the number of read bytes is returned and <*buf> is moved after the
294 * decoded data. See spoe_skip_data for details. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200295static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100296spoe_decode_data(char **buf, char *end, struct sample *smp)
297{
298 char *str, *p = *buf;
299 int type, r = 0;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200300 uint64_t sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100301
302 if (p >= end)
303 return -1;
304
305 type = *p++;
306 switch (type & SPOE_DATA_T_MASK) {
307 case SPOE_DATA_T_BOOL:
308 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
309 smp->data.type = SMP_T_BOOL;
310 break;
311 case SPOE_DATA_T_INT32:
312 case SPOE_DATA_T_INT64:
313 case SPOE_DATA_T_UINT32:
314 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200315 if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100316 return -1;
317 smp->data.type = SMP_T_SINT;
318 break;
319 case SPOE_DATA_T_IPV4:
320 if (p+4 > end)
321 return -1;
322 smp->data.type = SMP_T_IPV4;
323 memcpy(&smp->data.u.ipv4, p, 4);
324 p += 4;
325 break;
326 case SPOE_DATA_T_IPV6:
327 if (p+16 > end)
328 return -1;
329 memcpy(&smp->data.u.ipv6, p, 16);
330 smp->data.type = SMP_T_IPV6;
331 p += 16;
332 break;
333 case SPOE_DATA_T_STR:
334 case SPOE_DATA_T_BIN:
335 /* All the buffer must be decoded */
336 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
337 return -1;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200338 smp->data.u.str.area = str;
339 smp->data.u.str.data = sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100340 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
341 break;
342 }
343
344 r = (p - *buf);
345 *buf = p;
346 return r;
347}
348
349#endif /* _PROTO_SPOE_H */