blob: 002cf7d768fec1edd15417b3ef5fcd40b73e58cb [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: {
167 struct chunk *chk = &smp->data.u.str;
168
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. */
178 struct chunk *chk = &smp->data.u.str;
179
180 *p++ = (smp->data.type == SMP_T_STR)
181 ? SPOE_DATA_T_STR
182 : SPOE_DATA_T_BIN;
183 ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end);
184 if (ret == -1)
185 return -1;
186 }
187 else {
188 /* The sample has been fragmented, encode remaining data */
189 ret = MIN(chk->len - *off, end - p);
190 memcpy(p, chk->str + *off, ret);
191 p += ret;
192 }
193 /* Now update <*off> */
194 if (ret + *off != chk->len)
195 *off += ret;
196 else
197 *off = 0;
198 break;
199 }
200
201 case SMP_T_METH: {
202 char *m;
203 size_t len;
204
205 *p++ = SPOE_DATA_T_STR;
206 switch (smp->data.u.meth.meth) {
207 case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
208 case HTTP_METH_GET : m = "GET"; len = 3; break;
209 case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
210 case HTTP_METH_POST : m = "POST"; len = 4; break;
211 case HTTP_METH_PUT : m = "PUT"; len = 3; break;
212 case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
213 case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
214 case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
215
216 default :
217 m = smp->data.u.meth.str.str;
218 len = smp->data.u.meth.str.len;
219 }
220 if (spoe_encode_buffer(m, len, &p, end) == -1)
221 return -1;
222 break;
223 }
224
225 default:
226 *p++ = SPOE_DATA_T_NULL;
227 break;
228 }
229
230 end:
231 ret = (p - *buf);
232 *buf = p;
233 return ret;
234}
235
236/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
237 * of skipped bytes is returned and the <*buf> is moved after skipped data.
238 *
239 * A types data is composed of a type (1 byte) and corresponding data:
240 * - boolean: non additional data (0 bytes)
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200241 * - integers: a variable-length integer (see decode_varint)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100242 * - ipv4: 4 bytes
243 * - ipv6: 16 bytes
244 * - binary and string: a buffer prefixed by its size, a variable-length
245 * integer (see spoe_decode_buffer) */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200246static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100247spoe_skip_data(char **buf, char *end)
248{
249 char *str, *p = *buf;
250 int type, ret;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200251 uint64_t v, sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100252
253 if (p >= end)
254 return -1;
255
256 type = *p++;
257 switch (type & SPOE_DATA_T_MASK) {
258 case SPOE_DATA_T_BOOL:
259 break;
260 case SPOE_DATA_T_INT32:
261 case SPOE_DATA_T_INT64:
262 case SPOE_DATA_T_UINT32:
263 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200264 if (decode_varint(&p, end, &v) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100265 return -1;
266 break;
267 case SPOE_DATA_T_IPV4:
268 if (p+4 > end)
269 return -1;
270 p += 4;
271 break;
272 case SPOE_DATA_T_IPV6:
273 if (p+16 > end)
274 return -1;
275 p += 16;
276 break;
277 case SPOE_DATA_T_STR:
278 case SPOE_DATA_T_BIN:
279 /* All the buffer must be skipped */
280 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
281 return -1;
282 break;
283 }
284
285 ret = (p - *buf);
286 *buf = p;
287 return ret;
288}
289
290/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
291 * otherwise the number of read bytes is returned and <*buf> is moved after the
292 * decoded data. See spoe_skip_data for details. */
Thierry FOURNIERf4128a92017-04-09 05:41:27 +0200293static inline int
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100294spoe_decode_data(char **buf, char *end, struct sample *smp)
295{
296 char *str, *p = *buf;
297 int type, r = 0;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200298 uint64_t sz;
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100299
300 if (p >= end)
301 return -1;
302
303 type = *p++;
304 switch (type & SPOE_DATA_T_MASK) {
305 case SPOE_DATA_T_BOOL:
306 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
307 smp->data.type = SMP_T_BOOL;
308 break;
309 case SPOE_DATA_T_INT32:
310 case SPOE_DATA_T_INT64:
311 case SPOE_DATA_T_UINT32:
312 case SPOE_DATA_T_UINT64:
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200313 if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Faulet4ff3e572017-02-24 14:31:11 +0100314 return -1;
315 smp->data.type = SMP_T_SINT;
316 break;
317 case SPOE_DATA_T_IPV4:
318 if (p+4 > end)
319 return -1;
320 smp->data.type = SMP_T_IPV4;
321 memcpy(&smp->data.u.ipv4, p, 4);
322 p += 4;
323 break;
324 case SPOE_DATA_T_IPV6:
325 if (p+16 > end)
326 return -1;
327 memcpy(&smp->data.u.ipv6, p, 16);
328 smp->data.type = SMP_T_IPV6;
329 p += 16;
330 break;
331 case SPOE_DATA_T_STR:
332 case SPOE_DATA_T_BIN:
333 /* All the buffer must be decoded */
334 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
335 return -1;
336 smp->data.u.str.str = str;
337 smp->data.u.str.len = sz;
338 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
339 break;
340 }
341
342 r = (p - *buf);
343 *buf = p;
344 return r;
345}
346
347#endif /* _PROTO_SPOE_H */