blob: 2ef924f870d40d8a6ad68ee31ffb969e3208d8c8 [file] [log] [blame]
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001/*
2 * Stream processing offload engine management.
3 *
4 * Copyright 2016 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12#include <ctype.h>
13#include <errno.h>
14
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020015#include <common/cfgparse.h>
16#include <common/compat.h>
17#include <common/config.h>
18#include <common/debug.h>
19#include <common/memory.h>
20#include <common/time.h>
21
22#include <types/arg.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020023#include <types/global.h>
Christopher Faulet1f40b912017-02-17 09:32:19 +010024#include <types/spoe.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020025
26#include <proto/arg.h>
27#include <proto/backend.h>
28#include <proto/filters.h>
Christopher Faulet48026722016-11-16 15:01:12 +010029#include <proto/freq_ctr.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020030#include <proto/frontend.h>
31#include <proto/log.h>
32#include <proto/proto_http.h>
33#include <proto/proxy.h>
34#include <proto/sample.h>
35#include <proto/session.h>
36#include <proto/signal.h>
37#include <proto/stream.h>
38#include <proto/stream_interface.h>
39#include <proto/task.h>
40#include <proto/vars.h>
41
42#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
43#define SPOE_PRINTF(x...) fprintf(x)
44#else
45#define SPOE_PRINTF(x...)
46#endif
47
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010048/* Reserved 4 bytes to the frame size. So a frame and its size can be written
49 * together in a buffer */
50#define MAX_FRAME_SIZE global.tune.bufsize - 4
51
52/* The minimum size for a frame */
53#define MIN_FRAME_SIZE 256
54
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010055/* Reserved for the metadata and the frame type.
56 * So <MAX_FRAME_SIZE> - <FRAME_HDR_SIZE> is the maximum payload size */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010057#define FRAME_HDR_SIZE 32
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020058
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010059/* Helper to get SPOE ctx inside an appctx */
Christopher Faulet42bfa462017-01-04 14:14:19 +010060#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
61
Christopher Faulet3b386a32017-02-23 10:17:15 +010062/* SPOE filter id. Used to identify SPOE filters */
63const char *spoe_filter_id = "SPOE filter";
64
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020065/* Set if the handle on SIGUSR1 is registered */
66static int sighandler_registered = 0;
67
68/* proxy used during the parsing */
69struct proxy *curproxy = NULL;
70
71/* The name of the SPOE engine, used during the parsing */
72char *curengine = NULL;
73
74/* SPOE agent used during the parsing */
75struct spoe_agent *curagent = NULL;
76
77/* SPOE message used during the parsing */
78struct spoe_message *curmsg = NULL;
79
80/* list of SPOE messages and placeholders used during the parsing */
81struct list curmsgs;
82struct list curmps;
83
Christopher Faulet42bfa462017-01-04 14:14:19 +010084/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020085static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +010086static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020087
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020088struct flt_ops spoe_ops;
89
Christopher Faulet8ef75252017-02-20 22:56:03 +010090static int spoe_queue_context(struct spoe_context *ctx);
91static int spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
92static void spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020093
94/********************************************************************
95 * helper functions/globals
96 ********************************************************************/
97static void
Christopher Faulet8ef75252017-02-20 22:56:03 +010098spoe_release_msg_placeholder(struct spoe_msg_placeholder *mp)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020099{
100 if (!mp)
101 return;
102 free(mp->id);
103 free(mp);
104}
105
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200106static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100107spoe_release_message(struct spoe_message *msg)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200108{
109 struct spoe_arg *arg, *back;
110
111 if (!msg)
112 return;
113 free(msg->id);
114 free(msg->conf.file);
115 list_for_each_entry_safe(arg, back, &msg->args, list) {
116 release_sample_expr(arg->expr);
117 free(arg->name);
118 LIST_DEL(&arg->list);
119 free(arg);
120 }
121 free(msg);
122}
123
124static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100125spoe_release_agent(struct spoe_agent *agent)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200126{
127 struct spoe_message *msg, *back;
128 int i;
129
130 if (!agent)
131 return;
132 free(agent->id);
133 free(agent->conf.file);
134 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100135 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100136 free(agent->var_on_error);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200137 for (i = 0; i < SPOE_EV_EVENTS; ++i) {
138 list_for_each_entry_safe(msg, back, &agent->messages[i], list) {
139 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100140 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200141 }
142 }
143 free(agent);
144}
145
146static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100147 [SPOE_FRM_ERR_NONE] = "normal",
148 [SPOE_FRM_ERR_IO] = "I/O error",
149 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
150 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
151 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
152 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
153 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
154 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
155 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
156 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
157 [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
158 [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100159 [SPOE_FRM_ERR_FRAMEID_NOTFOUND] = "frame-id not found",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100160 [SPOE_FRM_ERR_RES] = "resource allocation error",
161 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200162};
163
164static const char *spoe_event_str[SPOE_EV_EVENTS] = {
165 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
166 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
167 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
168 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
169 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
170
171 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
172 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
173 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
174};
175
176
177#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
178
179static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100180 [SPOE_CTX_ST_NONE] = "NONE",
181 [SPOE_CTX_ST_READY] = "READY",
182 [SPOE_CTX_ST_ENCODING_MSGS] = "ENCODING_MSGS",
183 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
184 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
185 [SPOE_CTX_ST_DONE] = "DONE",
186 [SPOE_CTX_ST_ERROR] = "ERROR",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200187};
188
189static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100190 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
191 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
192 [SPOE_APPCTX_ST_IDLE] = "IDLE",
193 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
194 [SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY] = "SENDING_FRAG_NOTIFY",
195 [SPOE_APPCTX_ST_WAITING_SYNC_ACK] = "WAITING_SYNC_ACK",
196 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
197 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
198 [SPOE_APPCTX_ST_EXIT] = "EXIT",
199 [SPOE_APPCTX_ST_END] = "END",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200200};
201
202#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100203
Christopher Faulet8ef75252017-02-20 22:56:03 +0100204/* Used to generates a unique id for an engine. On success, it returns a
205 * allocated string. So it is the caller's reponsibility to release it. If the
206 * allocation failed, it returns NULL. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100207static char *
208generate_pseudo_uuid()
209{
210 static int init = 0;
211
212 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
213 const char uuid_chr[] = "0123456789ABCDEF-";
214 char *uuid;
215 int i;
216
217 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
218 return NULL;
219
220 if (!init) {
221 srand(now_ms);
222 init = 1;
223 }
224
225 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
226 int r = rand () % 16;
227
228 switch (uuid_fmt[i]) {
229 case 'x' : uuid[i] = uuid_chr[r]; break;
230 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
231 default : uuid[i] = uuid_fmt[i]; break;
232 }
233 }
234 return uuid;
235}
236
Christopher Faulet8ef75252017-02-20 22:56:03 +0100237/* Returns the minimum number of appets alive at a time. This function is used
238 * to know if more applets should be created for an engine. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100239static inline unsigned int
240min_applets_act(struct spoe_agent *agent)
241{
242 unsigned int nbsrv;
243
Christopher Faulet8ef75252017-02-20 22:56:03 +0100244 /* TODO: Add a config parameter to customize this value. Always 0 for
245 * now */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100246 if (agent->min_applets)
247 return agent->min_applets;
248
Christopher Faulet8ef75252017-02-20 22:56:03 +0100249 /* Get the number of active servers for the backend */
250 nbsrv = (agent->b.be->srv_act
251 ? agent->b.be->srv_act
252 : agent->b.be->srv_bck);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100253 return 2*nbsrv;
254}
255
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200256/********************************************************************
257 * Functions that encode/decode SPOE frames
258 ********************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200259/* Helper to get static string length, excluding the terminating null byte */
260#define SLEN(str) (sizeof(str)-1)
261
262/* Predefined key used in HELLO/DISCONNECT frames */
263#define SUPPORTED_VERSIONS_KEY "supported-versions"
264#define VERSION_KEY "version"
265#define MAX_FRAME_SIZE_KEY "max-frame-size"
266#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100267#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100268#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200269#define STATUS_CODE_KEY "status-code"
270#define MSG_KEY "message"
271
272struct spoe_version {
273 char *str;
274 int min;
275 int max;
276};
277
278/* All supported versions */
279static struct spoe_version supported_versions[] = {
280 {"1.0", 1000, 1000},
281 {NULL, 0, 0}
282};
283
284/* Comma-separated list of supported versions */
285#define SUPPORTED_VERSIONS_VAL "1.0"
286
287/* Comma-separated list of supported capabilities (none for now) */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100288#define CAPABILITIES_VAL "pipelining,async"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200289
Christopher Faulet8ef75252017-02-20 22:56:03 +0100290/* Convert a string to a SPOE version value. The string must follow the format
291 * "MAJOR.MINOR". It will be concerted into the integer (1000 * MAJOR + MINOR).
292 * If an error occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200293static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100294spoe_str_to_vsn(const char *str, size_t len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200295{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100296 const char *p, *end;
297 int maj, min, vsn;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200298
Christopher Faulet8ef75252017-02-20 22:56:03 +0100299 p = str;
300 end = str+len;
301 maj = min = 0;
302 vsn = -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200303
Christopher Faulet8ef75252017-02-20 22:56:03 +0100304 /* skip leading spaces */
305 while (p < end && isspace(*p))
306 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200307
Christopher Faulet8ef75252017-02-20 22:56:03 +0100308 /* parse Major number, until the '.' */
309 while (*p != '.') {
310 if (p >= end || *p < '0' || *p > '9')
311 goto out;
312 maj *= 10;
313 maj += (*p - '0');
314 p++;
315 }
316
317 /* check Major version */
318 if (!maj)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200319 goto out;
320
Christopher Faulet8ef75252017-02-20 22:56:03 +0100321 p++; /* skip the '.' */
322 if (p >= end || *p < '0' || *p > '9') /* Minor number is missing */
323 goto out;
324
325 /* Parse Minor number */
326 while (p < end) {
327 if (*p < '0' || *p > '9')
328 break;
329 min *= 10;
330 min += (*p - '0');
331 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200332 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100333
334 /* check Minor number */
335 if (min > 999)
336 goto out;
337
338 /* skip trailing spaces */
339 while (p < end && isspace(*p))
340 p++;
341 if (p != end)
342 goto out;
343
344 vsn = maj * 1000 + min;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200345 out:
346 return vsn;
347}
348
Christopher Faulet8ef75252017-02-20 22:56:03 +0100349/* Encode the integer <i> into a varint (variable-length integer). The encoded
350 * value is copied in <*buf>. Here is the encoding format:
351 *
352 * 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ]
353 * 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ]
354 * 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ]
355 * 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ]
356 * 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ]
357 * ...
358 *
359 * On success, it returns the number of written bytes and <*buf> is moved after
360 * the encoded value. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200361static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100362spoe_encode_varint(uint64_t i, char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200363{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100364 unsigned char *p = (unsigned char *)*buf;
365 int r;
366
367 if (p >= (unsigned char *)end)
368 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200369
370 if (i < 240) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100371 *p++ = i;
372 *buf = (char *)p;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200373 return 1;
374 }
375
Christopher Faulet8ef75252017-02-20 22:56:03 +0100376 *p++ = (unsigned char)i | 240;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200377 i = (i - 240) >> 4;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100378 while (i >= 128) {
379 if (p >= (unsigned char *)end)
380 return -1;
381 *p++ = (unsigned char)i | 128;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200382 i = (i - 128) >> 7;
383 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100384
385 if (p >= (unsigned char *)end)
386 return -1;
387 *p++ = (unsigned char)i;
388
389 r = ((char *)p - *buf);
390 *buf = (char *)p;
391 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200392}
393
Christopher Faulet8ef75252017-02-20 22:56:03 +0100394/* Decode a varint from <*buf> and save the decoded value in <*i>. See
395 * 'spoe_encode_varint' for details about varint.
396 * On success, it returns the number of read bytes and <*buf> is moved after the
397 * varint. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200398static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100399spoe_decode_varint(char **buf, char *end, uint64_t *i)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200400{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100401 unsigned char *p = (unsigned char *)*buf;
402 int r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200403
Christopher Faulet8ef75252017-02-20 22:56:03 +0100404 if (p >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200405 return -1;
406
Christopher Faulet8ef75252017-02-20 22:56:03 +0100407 *i = *p++;
408 if (*i < 240) {
409 *buf = (char *)p;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200410 return 1;
411 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100412
413 r = 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200414 do {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100415 if (p >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200416 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100417 *i += (uint64_t)*p << r;
418 r += 7;
419 } while (*p++ >= 128);
420
421 r = ((char *)p - *buf);
422 *buf = (char *)p;
423 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200424}
425
Christopher Faulet8ef75252017-02-20 22:56:03 +0100426/* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy
427 * of <str>. It must have enough space in <*buf> to encode the buffer, else an
428 * error is triggered.
429 * On success, it returns <len> and <*buf> is moved after the encoded value. If
430 * an error occurred, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200431static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100432spoe_encode_buffer(const char *str, size_t len, char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200433{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100434 char *p = *buf;
435 int ret;
436
437 if (p >= end)
438 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200439
440 if (!len) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100441 *p++ = 0;
442 *buf = p;
443 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200444 }
445
Christopher Faulet8ef75252017-02-20 22:56:03 +0100446 ret = spoe_encode_varint(len, &p, end);
447 if (ret == -1 || p + len > end)
448 return -1;
449
450 memcpy(p, str, len);
451 *buf = p + len;
452 return len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200453}
454
Christopher Faulet8ef75252017-02-20 22:56:03 +0100455/* Encode a buffer, possibly partially. It does the same thing than
456 * 'spoe_encode_buffer', but if there is not enough space, it does not fail.
457 * On success, it returns the number of copied bytes and <*buf> is moved after
458 * the encoded value. If an error occured, it returns -1. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100459static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100460spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100461{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100462 char *p = *buf;
463 int ret;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100464
Christopher Faulet8ef75252017-02-20 22:56:03 +0100465 if (p >= end)
466 return -1;
467
468 if (!len) {
469 *p++ = 0;
470 *buf = p;
471 return 0;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100472 }
473
Christopher Faulet8ef75252017-02-20 22:56:03 +0100474 ret = spoe_encode_varint(len, &p, end);
475 if (ret == -1 || p >= end)
476 return -1;
477
478 ret = (p+len < end) ? len : (end - p);
479 memcpy(p, str, ret);
480 *buf = p + ret;
481 return ret;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100482}
483
Christopher Faulet8ef75252017-02-20 22:56:03 +0100484/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str>
485 * points on the first byte of the buffer.
486 * On success, it returns the buffer length and <*buf> is moved after the
487 * encoded buffer. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200488static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100489spoe_decode_buffer(char **buf, char *end, char **str, size_t *len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200490{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100491 char *p = *buf;
492 uint64_t sz;
493 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200494
495 *str = NULL;
496 *len = 0;
497
Christopher Faulet8ef75252017-02-20 22:56:03 +0100498 ret = spoe_decode_varint(&p, end, &sz);
499 if (ret == -1 || p + sz > end)
500 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200501
Christopher Faulet8ef75252017-02-20 22:56:03 +0100502 *str = p;
503 *len = sz;
504 *buf = p + sz;
505 return sz;
506}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200507
Christopher Faulet8ef75252017-02-20 22:56:03 +0100508/* Encode a typed data using value in <smp>. On success, it returns the number
509 * of copied bytes and <*buf> is moved after the encoded value. If an error
510 * occured, it returns -1.
511 *
512 * If the value is too big to be encoded, depending on its type, then encoding
513 * failed or the value is partially encoded. Only strings and binaries can be
514 * partially encoded. In this case, the offset <*off> is updated to known how
515 * many bytes has been encoded. If <*off> is zero at the end, it means that all
516 * data has been encoded. */
517static int
518spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end)
519{
520 char *p = *buf;
521 int ret;
522
523 if (p >= end)
524 return -1;
525
526 if (smp == NULL) {
527 *p++ = SPOE_DATA_T_NULL;
528 goto end;
529 }
530
531 switch (smp->data.type) {
532 case SMP_T_BOOL:
533 *p = SPOE_DATA_T_BOOL;
534 *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
535 break;
536
537 case SMP_T_SINT:
538 *p++ = SPOE_DATA_T_INT64;
539 if (spoe_encode_varint(smp->data.u.sint, &p, end) == -1)
540 return -1;
541 break;
542
543 case SMP_T_IPV4:
544 if (p + 5 > end)
545 return -1;
546 *p++ = SPOE_DATA_T_IPV4;
547 memcpy(p, &smp->data.u.ipv4, 4);
548 p += 4;
549 break;
550
551 case SMP_T_IPV6:
552 if (p + 17 > end)
553 return -1;
554 *p++ = SPOE_DATA_T_IPV6;
555 memcpy(p, &smp->data.u.ipv6, 16);
556 p += 16;
557 break;
558
559 case SMP_T_STR:
560 case SMP_T_BIN: {
561 struct chunk *chk = &smp->data.u.str;
562
563 /* Here, we need to know if the sample has already been
564 * partially encoded. If yes, we only need to encode the
565 * remaining, <*off> reprensenting the number of bytes
566 * already encoded. */
567 if (!*off) {
568 /* First evaluation of the sample : encode the
569 * type (string or binary), the buffer length
570 * (as a varint) and at least 1 byte of the
571 * buffer. */
572 struct chunk *chk = &smp->data.u.str;
573
574 *p++ = (smp->data.type == SMP_T_STR)
575 ? SPOE_DATA_T_STR
576 : SPOE_DATA_T_BIN;
577 ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end);
578 if (ret == -1)
579 return -1;
580 }
581 else {
582 /* The sample has been fragmented, encode remaining data */
583 ret = MIN(chk->len - *off, end - p);
584 memcpy(p, chk->str + *off, ret);
585 p += ret;
586 }
587 /* Now update <*off> */
588 if (ret + *off != chk->len)
589 *off += ret;
590 else
591 *off = 0;
592 break;
593 }
594
595 case SMP_T_METH:
596 *p++ = SPOE_DATA_T_STR;
597 if (smp->data.u.meth.meth != HTTP_METH_OTHER) {
598 const struct http_method_name *meth =
599 &http_known_methods[smp->data.u.meth.meth];
600
601 if (spoe_encode_buffer(meth->name, meth->len, &p, end) == -1)
602 return -1;
603 }
604 else {
605 struct chunk *meth = &smp->data.u.meth.str;
606
607 if (spoe_encode_buffer(meth->str, meth->len, &p, end) == -1)
608 return -1;
609 }
610 break;
611
612 default:
613 *p++ = SPOE_DATA_T_NULL;
614 break;
615 }
616
617 end:
618 ret = (p - *buf);
619 *buf = p;
620 return ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200621}
622
623/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
Christopher Faulet8ef75252017-02-20 22:56:03 +0100624 * of skipped bytes is returned and the <*buf> is moved after skipped data.
625 *
626 * A types data is composed of a type (1 byte) and corresponding data:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200627 * - boolean: non additional data (0 bytes)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100628 * - integers: a variable-length integer (see spoe_decode_varint)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200629 * - ipv4: 4 bytes
630 * - ipv6: 16 bytes
631 * - binary and string: a buffer prefixed by its size, a variable-length
Christopher Faulet8ef75252017-02-20 22:56:03 +0100632 * integer (see spoe_decode_buffer) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200633static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100634spoe_skip_data(char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200635{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100636 char *str, *p = *buf;
637 int type, ret;
638 size_t sz;
639 uint64_t v;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200640
Christopher Faulet8ef75252017-02-20 22:56:03 +0100641 if (p >= end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200642 return -1;
643
Christopher Faulet8ef75252017-02-20 22:56:03 +0100644 type = *p++;
645 switch (type & SPOE_DATA_T_MASK) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200646 case SPOE_DATA_T_BOOL:
647 break;
648 case SPOE_DATA_T_INT32:
649 case SPOE_DATA_T_INT64:
650 case SPOE_DATA_T_UINT32:
651 case SPOE_DATA_T_UINT64:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100652 if (spoe_decode_varint(&p, end, &v) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200653 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200654 break;
655 case SPOE_DATA_T_IPV4:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100656 if (p+4 > end)
657 return -1;
658 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200659 break;
660 case SPOE_DATA_T_IPV6:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100661 if (p+16 > end)
662 return -1;
663 p += 16;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200664 break;
665 case SPOE_DATA_T_STR:
666 case SPOE_DATA_T_BIN:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100667 /* All the buffer must be skipped */
668 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200669 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200670 break;
671 }
672
Christopher Faulet8ef75252017-02-20 22:56:03 +0100673 ret = (p - *buf);
674 *buf = p;
675 return ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200676}
677
Christopher Faulet8ef75252017-02-20 22:56:03 +0100678/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
679 * otherwise the number of read bytes is returned and <*buf> is moved after the
680 * decoded data. See spoe_skip_data for details. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200681static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100682spoe_decode_data(char **buf, char *end, struct sample *smp)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200683{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100684 char *str, *p = *buf;
685 int type, r = 0;
686 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200687
Christopher Faulet8ef75252017-02-20 22:56:03 +0100688 if (p >= end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200689 return -1;
690
Christopher Faulet8ef75252017-02-20 22:56:03 +0100691 type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200692 switch (type & SPOE_DATA_T_MASK) {
693 case SPOE_DATA_T_BOOL:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100694 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200695 smp->data.type = SMP_T_BOOL;
696 break;
697 case SPOE_DATA_T_INT32:
698 case SPOE_DATA_T_INT64:
699 case SPOE_DATA_T_UINT32:
700 case SPOE_DATA_T_UINT64:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100701 if (spoe_decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200702 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200703 smp->data.type = SMP_T_SINT;
704 break;
705 case SPOE_DATA_T_IPV4:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100706 if (p+4 > end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200707 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200708 smp->data.type = SMP_T_IPV4;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100709 memcpy(&smp->data.u.ipv4, p, 4);
710 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200711 break;
712 case SPOE_DATA_T_IPV6:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100713 if (p+16 > end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200714 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100715 memcpy(&smp->data.u.ipv6, p, 16);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200716 smp->data.type = SMP_T_IPV6;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100717 p += 16;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200718 break;
719 case SPOE_DATA_T_STR:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200720 case SPOE_DATA_T_BIN:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100721 /* All the buffer must be decoded */
722 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200723 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100724 smp->data.u.str.str = str;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200725 smp->data.u.str.len = sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100726 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200727 break;
728 }
729
Christopher Faulet8ef75252017-02-20 22:56:03 +0100730 r = (p - *buf);
731 *buf = p;
732 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200733}
734
Christopher Faulet8ef75252017-02-20 22:56:03 +0100735/* Encode the HELLO frame sent by HAProxy to an agent. It returns the number of
736 * encoded bytes in the frame on success, 0 if an encoding error occured and -1
737 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200738static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100739spoe_prepare_hahello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200740{
Christopher Faulet42bfa462017-01-04 14:14:19 +0100741 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100742 char *p, *end;
743 unsigned int flags = SPOE_FRM_FL_FIN;
744 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200745
Christopher Faulet8ef75252017-02-20 22:56:03 +0100746 p = frame;
747 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200748
Christopher Faulet8ef75252017-02-20 22:56:03 +0100749 /* Set Frame type */
750 *p++ = SPOE_FRM_T_HAPROXY_HELLO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200751
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100752 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100753 memcpy(p, (char *)&flags, 4);
754 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200755
756 /* No stream-id and frame-id for HELLO frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100757 *p++ = 0; *p++ = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200758
759 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
760 * and "capabilities" */
761
762 /* "supported-versions" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100763 sz = SLEN(SUPPORTED_VERSIONS_KEY);
764 if (spoe_encode_buffer(SUPPORTED_VERSIONS_KEY, sz, &p, end) == -1)
765 goto too_big;
766
767 *p++ = SPOE_DATA_T_STR;
768 sz = SLEN(SUPPORTED_VERSIONS_VAL);
769 if (spoe_encode_buffer(SUPPORTED_VERSIONS_VAL, sz, &p, end) == -1)
770 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200771
772 /* "max-fram-size" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100773 sz = SLEN(MAX_FRAME_SIZE_KEY);
774 if (spoe_encode_buffer(MAX_FRAME_SIZE_KEY, sz, &p, end) == -1)
775 goto too_big;
776
777 *p++ = SPOE_DATA_T_UINT32;
778 if (spoe_encode_varint(SPOE_APPCTX(appctx)->max_frame_size, &p, end) == -1)
779 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200780
781 /* "capabilities" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100782 sz = SLEN(CAPABILITIES_KEY);
783 if (spoe_encode_buffer(CAPABILITIES_KEY, sz, &p, end) == -1)
784 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200785
Christopher Faulet8ef75252017-02-20 22:56:03 +0100786 *p++ = SPOE_DATA_T_STR;
787 sz = SLEN(CAPABILITIES_VAL);
788 if (spoe_encode_buffer(CAPABILITIES_VAL, sz, &p, end) == -1)
789 goto too_big;
790
791 /* (optionnal) "engine-id" K/V item, if present */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100792 if (agent != NULL && agent->engine_id != NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100793 sz = SLEN(ENGINE_ID_KEY);
794 if (spoe_encode_buffer(ENGINE_ID_KEY, sz, &p, end) == -1)
795 goto too_big;
796
797 *p++ = SPOE_DATA_T_STR;
798 sz = strlen(agent->engine_id);
799 if (spoe_encode_buffer(agent->engine_id, sz, &p, end) == -1)
800 goto too_big;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100801 }
802
Christopher Faulet8ef75252017-02-20 22:56:03 +0100803 return (p - frame);
804
805 too_big:
806 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
807 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200808}
809
Christopher Faulet8ef75252017-02-20 22:56:03 +0100810/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the number of
811 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
812 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200813static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100814spoe_prepare_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200815{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100816 const char *reason;
817 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100818 unsigned int flags = SPOE_FRM_FL_FIN;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100819 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200820
Christopher Faulet8ef75252017-02-20 22:56:03 +0100821 p = frame;
822 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200823
Christopher Faulet8ef75252017-02-20 22:56:03 +0100824 /* Set Frame type */
825 *p++ = SPOE_FRM_T_HAPROXY_DISCON;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200826
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100827 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100828 memcpy(p, (char *)&flags, 4);
829 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200830
831 /* No stream-id and frame-id for DISCONNECT frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100832 *p++ = 0; *p++ = 0;
833
834 if (SPOE_APPCTX(appctx)->status_code >= SPOE_FRM_ERRS)
835 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200836
837 /* There are 2 mandatory items: "status-code" and "message" */
838
839 /* "status-code" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100840 sz = SLEN(STATUS_CODE_KEY);
841 if (spoe_encode_buffer(STATUS_CODE_KEY, sz, &p, end) == -1)
842 goto too_big;
843
844 *p++ = SPOE_DATA_T_UINT32;
845 if (spoe_encode_varint(SPOE_APPCTX(appctx)->status_code, &p, end) == -1)
846 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200847
848 /* "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100849 sz = SLEN(MSG_KEY);
850 if (spoe_encode_buffer(MSG_KEY, sz, &p, end) == -1)
851 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200852
Christopher Faulet8ef75252017-02-20 22:56:03 +0100853 /*Get the message corresponding to the status code */
854 reason = spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code];
855
856 *p++ = SPOE_DATA_T_STR;
857 sz = strlen(reason);
858 if (spoe_encode_buffer(reason, sz, &p, end) == -1)
859 goto too_big;
860
861 return (p - frame);
862
863 too_big:
864 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
865 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200866}
867
Christopher Faulet8ef75252017-02-20 22:56:03 +0100868/* Encode the NOTIFY frame sent by HAProxy to an agent. It returns the number of
869 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
870 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200871static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100872spoe_prepare_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
Christopher Fauleta1cda022016-12-21 08:58:06 +0100873 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200874{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100875 char *p, *end;
876 unsigned int stream_id, frame_id;
877 unsigned int flags = SPOE_FRM_FL_FIN;
878 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200879
Christopher Faulet8ef75252017-02-20 22:56:03 +0100880 p = frame;
881 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200882
Christopher Faulet8ef75252017-02-20 22:56:03 +0100883 /* <ctx> is null when the stream has aborted the processing of a
884 * fragmented frame. In this case, we must notify the corresponding
885 * agent using ids stored in <frag_ctx>. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100886 if (ctx == NULL) {
887 flags |= SPOE_FRM_FL_ABRT;
888 stream_id = SPOE_APPCTX(appctx)->frag_ctx.cursid;
889 frame_id = SPOE_APPCTX(appctx)->frag_ctx.curfid;
890 }
891 else {
892 stream_id = ctx->stream_id;
893 frame_id = ctx->frame_id;
894
895 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100896 /* The fragmentation is not supported by the applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100897 if (!(SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_FRAGMENTATION)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100898 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
899 return -1;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100900 }
901 flags = ctx->frag_ctx.flags;
902 }
903 }
904
Christopher Faulet8ef75252017-02-20 22:56:03 +0100905 /* Set Frame type */
906 *p++ = SPOE_FRM_T_HAPROXY_NOTIFY;
907
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100908 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100909 memcpy(p, (char *)&flags, 4);
910 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200911
912 /* Set stream-id and frame-id */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100913 if (spoe_encode_varint(stream_id, &p, end) == -1)
914 goto too_big;
915 if (spoe_encode_varint(frame_id, &p, end) == -1)
916 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200917
Christopher Faulet8ef75252017-02-20 22:56:03 +0100918 /* Copy encoded messages, if possible */
919 sz = SPOE_APPCTX(appctx)->buffer->i;
920 if (p + sz >= end)
921 goto too_big;
922 memcpy(p, SPOE_APPCTX(appctx)->buffer->p, sz);
923 p += sz;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100924
Christopher Faulet8ef75252017-02-20 22:56:03 +0100925 return (p - frame);
926
927 too_big:
928 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
929 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200930}
931
Christopher Faulet8ef75252017-02-20 22:56:03 +0100932/* Decode and process the HELLO frame sent by an agent. It returns the number of
933 * read bytes on success, 0 if a decoding error occurred, and -1 if a fatal
934 * error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200935static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100936spoe_handle_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200937{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100938 char *p, *end;
939 int vsn, max_frame_size;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100940 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100941
942 p = frame;
943 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200944
945 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100946 if (*p++ != SPOE_FRM_T_AGENT_HELLO) {
947 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200948 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100949 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200950
Christopher Faulet8ef75252017-02-20 22:56:03 +0100951 if (size < 7 /* TYPE + METADATA */) {
952 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
953 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200954 }
955
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100956 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100957 memcpy((char *)&flags, p, 4);
958 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200959
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100960 /* Fragmentation is not supported for HELLO frame */
961 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100962 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100963 return -1;
964 }
965
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200966 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100967 if (*p != 0 || *(p+1) != 0) {
968 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
969 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200970 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100971 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200972
973 /* There are 3 mandatory items: "version", "max-frame-size" and
974 * "capabilities" */
975
976 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100977 vsn = max_frame_size = flags = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100978 while (p < end) {
979 char *str;
980 size_t sz;
981 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200982
983 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100984 ret = spoe_decode_buffer(&p, end, &str, &sz);
985 if (ret == -1 || !sz) {
986 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
987 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200988 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100989
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200990 /* Check "version" K/V item */
991 if (!memcmp(str, VERSION_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100992 int i, type = *p++;
993
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200994 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100995 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
996 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
997 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200998 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100999 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
1000 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1001 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001002 }
1003
Christopher Faulet8ef75252017-02-20 22:56:03 +01001004 vsn = spoe_str_to_vsn(str, sz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001005 if (vsn == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001006 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001007 return -1;
1008 }
1009 for (i = 0; supported_versions[i].str != NULL; ++i) {
1010 if (vsn >= supported_versions[i].min &&
1011 vsn <= supported_versions[i].max)
1012 break;
1013 }
1014 if (supported_versions[i].str == NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001015 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001016 return -1;
1017 }
1018 }
1019 /* Check "max-frame-size" K/V item */
1020 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001021 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001022
1023 /* The value must be integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001024 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1025 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1026 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1027 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001028 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1029 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001030 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001031 if (spoe_decode_varint(&p, end, &sz) == -1) {
1032 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1033 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001034 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001035 if (sz < MIN_FRAME_SIZE ||
1036 sz > SPOE_APPCTX(appctx)->max_frame_size) {
1037 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001038 return -1;
1039 }
1040 max_frame_size = sz;
1041 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001042 /* Check "capabilities" K/V item */
1043 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001044 int type = *p++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001045
1046 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001047 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1048 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1049 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001050 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001051 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
1052 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1053 return 0;
1054 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001055
Christopher Faulet8ef75252017-02-20 22:56:03 +01001056 while (sz) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001057 char *delim;
1058
1059 /* Skip leading spaces */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001060 for (; isspace(*str) && sz; str++, sz--);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001061
Christopher Faulet8ef75252017-02-20 22:56:03 +01001062 if (sz >= 10 && !strncmp(str, "pipelining", 10)) {
1063 str += 10; sz -= 10;
1064 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +01001065 flags |= SPOE_APPCTX_FL_PIPELINING;
1066 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001067 else if (sz >= 5 && !strncmp(str, "async", 5)) {
1068 str += 5; sz -= 5;
1069 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +01001070 flags |= SPOE_APPCTX_FL_ASYNC;
1071 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001072 else if (sz >= 13 && !strncmp(str, "fragmentation", 13)) {
1073 str += 13; sz -= 13;
1074 if (!sz || isspace(*str) || *str == ',')
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001075 flags |= SPOE_APPCTX_FL_FRAGMENTATION;
1076 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001077
Christopher Faulet8ef75252017-02-20 22:56:03 +01001078 /* Get the next comma or break */
1079 if (!sz || (delim = memchr(str, ',', sz)) == NULL)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001080 break;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001081 delim++;
1082 sz -= (delim - str);
1083 str = delim;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001084 }
1085 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001086 else {
1087 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001088 if (spoe_skip_data(&p, end) == -1) {
1089 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1090 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001091 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001092 }
1093 }
1094
1095 /* Final checks */
1096 if (!vsn) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001097 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001098 return -1;
1099 }
1100 if (!max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001101 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001102 return -1;
1103 }
1104
Christopher Faulet42bfa462017-01-04 14:14:19 +01001105 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
1106 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
1107 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001108
1109 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001110}
1111
1112/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
1113 * bytes on success, 0 if the frame can be ignored and -1 if an error
1114 * occurred. */
1115static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001116spoe_handle_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001117{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001118 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001119 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001120
1121 p = frame;
1122 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001123
1124 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001125 if (*p++ != SPOE_FRM_T_AGENT_DISCON) {
1126 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001127 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001128 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001129
Christopher Faulet8ef75252017-02-20 22:56:03 +01001130 if (size < 7 /* TYPE + METADATA */) {
1131 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1132 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001133 }
1134
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001135 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001136 memcpy((char *)&flags, p, 4);
1137 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001138
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001139 /* Fragmentation is not supported for DISCONNECT frame */
1140 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001141 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001142 return -1;
1143 }
1144
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001145 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001146 if (*p != 0 || *(p+1) != 0) {
1147 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1148 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001149 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001150 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001151
1152 /* There are 2 mandatory items: "status-code" and "message" */
1153
1154 /* Loop on K/V items */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001155 while (p < end) {
1156 char *str;
1157 size_t sz;
1158 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001159
1160 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001161 ret = spoe_decode_buffer(&p, end, &str, &sz);
1162 if (ret == -1 || !sz) {
1163 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1164 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001165 }
1166
1167 /* Check "status-code" K/V item */
1168 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001169 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001170
1171 /* The value must be an integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001172 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1173 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1174 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1175 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001176 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1177 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001178 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001179 if (spoe_decode_varint(&p, end, &sz) == -1) {
1180 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1181 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001182 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001183 SPOE_APPCTX(appctx)->status_code = sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001184 }
1185
1186 /* Check "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001187 else if (!memcmp(str, MSG_KEY, sz)) {
1188 int type = *p++;
1189
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001190 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001191 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1192 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1193 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001194 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001195 ret = spoe_decode_buffer(&p, end, &str, &sz);
1196 if (ret == -1 || sz > 255) {
1197 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1198 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001199 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001200#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
1201 SPOE_APPCTX(appctx)->reason = str;
1202 SPOE_APPCTX(appctx)->rlen = sz;
1203#endif
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001204 }
1205 else {
1206 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001207 if (spoe_skip_data(&p, end) == -1) {
1208 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1209 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001210 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001211 }
1212 }
1213
Christopher Faulet8ef75252017-02-20 22:56:03 +01001214 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001215}
1216
1217
Christopher Fauleta1cda022016-12-21 08:58:06 +01001218/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001219 * success, 0 if the frame can be ignored and -1 if an error occurred. */
1220static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001221spoe_handle_agentack_frame(struct appctx *appctx, struct spoe_context **ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001222 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001223{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001224 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001225 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001226 uint64_t stream_id, frame_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001227 int len;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001228 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001229
1230 p = frame;
1231 end = frame + size;
1232 *ctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001233
1234 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001235 if (*p++ != SPOE_FRM_T_AGENT_ACK) {
1236 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001237 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001238 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001239
Christopher Faulet8ef75252017-02-20 22:56:03 +01001240 if (size < 7 /* TYPE + METADATA */) {
1241 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1242 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001243 }
1244
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001245 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001246 memcpy((char *)&flags, p, 4);
1247 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001248
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001249 /* Fragmentation is not supported for now */
1250 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001251 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001252 return -1;
1253 }
1254
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001255 /* Get the stream-id and the frame-id */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001256 if (spoe_decode_varint(&p, end, &stream_id) == -1) {
1257 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001258 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001259 }
1260 if (spoe_decode_varint(&p, end, &frame_id) == -1) {
1261 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001262 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001263 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001264
Christopher Faulet8ef75252017-02-20 22:56:03 +01001265 /* Try to find the corresponding SPOE context */
Christopher Faulet42bfa462017-01-04 14:14:19 +01001266 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001267 list_for_each_entry((*ctx), &agent->waiting_queue, list) {
1268 if ((*ctx)->stream_id == (unsigned int)stream_id &&
1269 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001270 goto found;
1271 }
1272 }
1273 else {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001274 list_for_each_entry((*ctx), &SPOE_APPCTX(appctx)->waiting_queue, list) {
1275 if ((*ctx)->stream_id == (unsigned int)stream_id &&
Christopher Faulet8ef75252017-02-20 22:56:03 +01001276 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001277 goto found;
1278 }
1279 }
1280
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001281 if (SPOE_APPCTX(appctx)->frag_ctx.ctx &&
1282 SPOE_APPCTX(appctx)->frag_ctx.cursid == (unsigned int)stream_id &&
1283 SPOE_APPCTX(appctx)->frag_ctx.curfid == (unsigned int)frame_id) {
1284
1285 /* ABRT bit is set for an unfinished fragmented frame */
1286 if (flags & SPOE_FRM_FL_ABRT) {
1287 *ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1288 (*ctx)->frag_ctx.spoe_appctx = NULL;
1289 (*ctx)->state = SPOE_CTX_ST_ERROR;
1290 (*ctx)->status_code = SPOE_CTX_ERR_FRAG_FRAME_ABRT;
1291 /* Ignore the payload */
1292 goto end;
1293 }
1294 /* TODO: Handle more flags for fragmented frames: RESUME, FINISH... */
1295 /* For now, we ignore the ack */
1296 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1297 return 0;
1298 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001299
Christopher Fauleta1cda022016-12-21 08:58:06 +01001300 /* No Stream found, ignore the frame */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001301 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1302 " - Ignore ACK frame"
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001303 " - stream-id=%u - frame-id=%u\n",
1304 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1305 __FUNCTION__, appctx,
1306 (unsigned int)stream_id, (unsigned int)frame_id);
1307
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001308 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAMEID_NOTFOUND;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001309 return 0;
1310
1311 found:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001312 if (!spoe_acquire_buffer(&SPOE_APPCTX(appctx)->buffer,
1313 &SPOE_APPCTX(appctx)->buffer_wait)) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001314 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001315 return 1; /* Retry later */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001316 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001317
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001318 /* Copy encoded actions */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001319 len = (end - p);
1320 memcpy(SPOE_APPCTX(appctx)->buffer->p, p, len);
1321 SPOE_APPCTX(appctx)->buffer->i = len;
1322 p += len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001323
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001324 /* Transfer the buffer ownership to the SPOE context */
1325 (*ctx)->buffer = SPOE_APPCTX(appctx)->buffer;
1326 SPOE_APPCTX(appctx)->buffer = &buf_empty;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001327
Christopher Faulet8ef75252017-02-20 22:56:03 +01001328 (*ctx)->state = SPOE_CTX_ST_DONE;
1329
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001330 end:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001331 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001332 " - ACK frame received"
1333 " - ctx=%p - stream-id=%u - frame-id=%u - flags=0x%08x\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001334 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001335 __FUNCTION__, appctx, *ctx, (*ctx)->stream_id,
1336 (*ctx)->frame_id, flags);
1337 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001338}
1339
Christopher Fauletba7bc162016-11-07 21:07:38 +01001340/* This function is used in cfgparse.c and declared in proto/checks.h. It
1341 * prepare the request to send to agents during a healthcheck. It returns 0 on
1342 * success and -1 if an error occurred. */
1343int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001344spoe_prepare_healthcheck_request(char **req, int *len)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001345{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001346 struct appctx appctx;
1347 struct spoe_appctx spoe_appctx;
1348 char *frame, *end, buf[MAX_FRAME_SIZE+4];
1349 size_t sz;
1350 int ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001351
Christopher Faulet42bfa462017-01-04 14:14:19 +01001352 memset(&appctx, 0, sizeof(appctx));
1353 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001354 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001355
1356 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001357 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001358
Christopher Faulet8ef75252017-02-20 22:56:03 +01001359 frame = buf+4; /* Reserved the 4 first bytes for the frame size */
1360 end = frame + MAX_FRAME_SIZE;
1361
1362 ret = spoe_prepare_hahello_frame(&appctx, frame, MAX_FRAME_SIZE);
1363 if (ret <= 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001364 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001365 frame += ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001366
Christopher Faulet8ef75252017-02-20 22:56:03 +01001367 /* Add "healthcheck" K/V item */
1368 sz = SLEN(HEALTHCHECK_KEY);
1369 if (spoe_encode_buffer(HEALTHCHECK_KEY, sz, &frame, end) == -1)
1370 return -1;
1371 *frame++ = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001372
Christopher Faulet8ef75252017-02-20 22:56:03 +01001373 *len = frame - buf;
1374 sz = htonl(*len - 4);
1375 memcpy(buf, (char *)&sz, 4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001376
Christopher Faulet8ef75252017-02-20 22:56:03 +01001377 if ((*req = malloc(*len)) == NULL)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001378 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001379 memcpy(*req, buf, *len);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001380 return 0;
1381}
1382
1383/* This function is used in checks.c and declared in proto/checks.h. It decode
1384 * the response received from an agent during a healthcheck. It returns 0 on
1385 * success and -1 if an error occurred. */
1386int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001387spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001388{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001389 struct appctx appctx;
1390 struct spoe_appctx spoe_appctx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001391
Christopher Faulet42bfa462017-01-04 14:14:19 +01001392 memset(&appctx, 0, sizeof(appctx));
1393 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001394
Christopher Faulet42bfa462017-01-04 14:14:19 +01001395 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001396 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001397
Christopher Faulet8ef75252017-02-20 22:56:03 +01001398 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1399 spoe_handle_agentdiscon_frame(&appctx, frame, size);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001400 goto error;
1401 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001402 if (spoe_handle_agenthello_frame(&appctx, frame, size) <= 0)
1403 goto error;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001404
1405 return 0;
1406
1407 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001408 if (SPOE_APPCTX(&appctx)->status_code >= SPOE_FRM_ERRS)
1409 SPOE_APPCTX(&appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
1410 strncpy(err, spoe_frm_err_reasons[SPOE_APPCTX(&appctx)->status_code], errlen);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001411 return -1;
1412}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001413
Christopher Fauleta1cda022016-12-21 08:58:06 +01001414/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1415 * the frame can be ignored, 1 to retry later, and the frame legnth on
1416 * success. */
1417static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001418spoe_send_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001419{
1420 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001421 int ret;
1422 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001423
1424 if (si_ic(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001425 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001426
Christopher Faulet8ef75252017-02-20 22:56:03 +01001427 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1428 * length. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001429 netint = htonl(framesz);
1430 memcpy(buf, (char *)&netint, 4);
1431 ret = bi_putblk(si_ic(si), buf, framesz+4);
1432
1433 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001434 if (ret == -1) {
1435 retry:
1436 si_applet_cant_put(si);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001437 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001438 }
1439 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001440 return -1; /* error */
1441 }
1442 return framesz;
1443}
1444
1445/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1446 * when the frame can be ignored, 1 to retry later and the frame length on
1447 * success. */
1448static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001449spoe_recv_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001450{
1451 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001452 int ret;
1453 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001454
1455 if (si_oc(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001456 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001457
1458 ret = bo_getblk(si_oc(si), (char *)&netint, 4, 0);
1459 if (ret > 0) {
1460 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001461 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001462 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001463 return -1;
1464 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001465 ret = bo_getblk(si_oc(si), buf, framesz, 4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001466 }
1467 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001468 if (ret == 0) {
1469 retry:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001470 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001471 }
1472 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001473 return -1; /* error */
1474 }
1475 return framesz;
1476}
1477
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001478/********************************************************************
1479 * Functions that manage the SPOE applet
1480 ********************************************************************/
Christopher Faulet4596fb72017-01-11 14:05:19 +01001481static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001482spoe_wakeup_appctx(struct appctx *appctx)
Christopher Faulet4596fb72017-01-11 14:05:19 +01001483{
1484 si_applet_want_get(appctx->owner);
1485 si_applet_want_put(appctx->owner);
1486 appctx_wakeup(appctx);
1487 return 1;
1488}
1489
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001490/* Callback function that catches applet timeouts. If a timeout occurred, we set
1491 * <appctx->st1> flag and the SPOE applet is woken up. */
1492static struct task *
Christopher Faulet8ef75252017-02-20 22:56:03 +01001493spoe_process_appctx(struct task * task)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001494{
1495 struct appctx *appctx = task->context;
1496
1497 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1498 if (tick_is_expired(task->expire, now_ms)) {
1499 task->expire = TICK_ETERNITY;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001500 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001501 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001502 spoe_wakeup_appctx(appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001503 return task;
1504}
1505
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001506/* Callback function that releases a SPOE applet. This happens when the
1507 * connection with the agent is closed. */
1508static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001509spoe_release_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001510{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001511 struct stream_interface *si = appctx->owner;
1512 struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
1513 struct spoe_agent *agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001514 struct spoe_context *ctx, *back;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001515
1516 if (spoe_appctx == NULL)
1517 return;
1518
1519 appctx->ctx.spoe.ptr = NULL;
1520 agent = spoe_appctx->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001521
1522 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1523 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1524 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001525
Christopher Faulet8ef75252017-02-20 22:56:03 +01001526 /* Remove applet from the list of running applets */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001527 agent->applets_act--;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001528 if (!LIST_ISEMPTY(&spoe_appctx->list)) {
1529 LIST_DEL(&spoe_appctx->list);
1530 LIST_INIT(&spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001531 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001532
Christopher Faulet8ef75252017-02-20 22:56:03 +01001533 /* Shutdown the server connection, if needed */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001534 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001535 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1536 agent->applets_idle--;
1537
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001538 si_shutw(si);
1539 si_shutr(si);
1540 si_ic(si)->flags |= CF_READ_NULL;
1541 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001542 if (spoe_appctx->status_code == SPOE_FRM_ERR_NONE)
1543 spoe_appctx->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001544 }
1545
Christopher Faulet8ef75252017-02-20 22:56:03 +01001546 /* Destroy the task attached to this applet */
1547 if (spoe_appctx->task) {
1548 task_delete(spoe_appctx->task);
1549 task_free(spoe_appctx->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001550 }
1551
Christopher Faulet8ef75252017-02-20 22:56:03 +01001552 /* Notify all waiting streams */
1553 list_for_each_entry_safe(ctx, back, &spoe_appctx->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001554 LIST_DEL(&ctx->list);
1555 LIST_INIT(&ctx->list);
1556 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001557 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001558 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001559 }
1560
Christopher Faulet8ef75252017-02-20 22:56:03 +01001561 /* If the applet was processing a fragmented frame, notify the
1562 * corresponding stream. */
1563 if (spoe_appctx->frag_ctx.ctx) {
1564 ctx = spoe_appctx->frag_ctx.ctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001565 ctx->frag_ctx.spoe_appctx = NULL;
1566 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001567 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001568 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1569 }
1570
Christopher Faulet8ef75252017-02-20 22:56:03 +01001571 /* Release allocated memory */
1572 spoe_release_buffer(&spoe_appctx->buffer,
1573 &spoe_appctx->buffer_wait);
1574 pool_free2(pool2_spoe_appctx, spoe_appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001575
Christopher Fauleta1cda022016-12-21 08:58:06 +01001576 if (!LIST_ISEMPTY(&agent->applets))
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001577 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001578
Christopher Faulet8ef75252017-02-20 22:56:03 +01001579 /* If this was the last running applet, notify all waiting streams */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001580 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1581 LIST_DEL(&ctx->list);
1582 LIST_INIT(&ctx->list);
1583 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001584 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001585 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001586 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001587 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1588 LIST_DEL(&ctx->list);
1589 LIST_INIT(&ctx->list);
1590 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001591 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001592 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1593 }
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001594
1595 end:
1596 /* Update runtinme agent info */
1597 agent->frame_size = agent->max_frame_size;
1598 list_for_each_entry(spoe_appctx, &agent->applets, list)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001599 agent->frame_size = MIN(spoe_appctx->max_frame_size,
1600 agent->frame_size);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001601}
1602
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001603static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001604spoe_handle_connect_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001605{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001606 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001607 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001608 char *frame, *buf;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001609 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001610
Christopher Fauleta1cda022016-12-21 08:58:06 +01001611 if (si->state <= SI_ST_CON) {
1612 si_applet_want_put(si);
1613 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1614 goto stop;
1615 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001616 if (si->state != SI_ST_EST) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001617 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001618 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001619 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001620
Christopher Fauleta1cda022016-12-21 08:58:06 +01001621 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001622 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1623 " - Connection timed out\n",
1624 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1625 __FUNCTION__, appctx);
1626 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001627 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001628 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001629
Christopher Faulet42bfa462017-01-04 14:14:19 +01001630 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001631 SPOE_APPCTX(appctx)->task->expire =
1632 tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001633
Christopher Faulet8ef75252017-02-20 22:56:03 +01001634 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1635 * length. */
1636 buf = trash.str; frame = buf+4;
1637 ret = spoe_prepare_hahello_frame(appctx, frame,
1638 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001639 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001640 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001641
1642 switch (ret) {
1643 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001644 case 0: /* ignore => an error, cannot be ignored */
1645 goto exit;
1646
1647 case 1: /* retry later */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001648 goto stop;
1649
Christopher Faulet8ef75252017-02-20 22:56:03 +01001650 default:
1651 /* HELLO frame successfully sent, now wait for the
1652 * reply. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001653 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1654 goto next;
1655 }
1656
1657 next:
1658 return 0;
1659 stop:
1660 return 1;
1661 exit:
1662 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1663 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001664}
1665
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001666static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001667spoe_handle_connecting_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001668{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001669 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001670 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001671 char *frame;
1672 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001673
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001674
Christopher Fauletb067b062017-01-04 16:39:11 +01001675 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001676 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001677 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001678 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001679
Christopher Fauleta1cda022016-12-21 08:58:06 +01001680 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001681 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1682 " - Connection timed out\n",
1683 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1684 __FUNCTION__, appctx);
1685 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001686 goto exit;
1687 }
1688
Christopher Faulet8ef75252017-02-20 22:56:03 +01001689 frame = trash.str; trash.len = 0;
1690 ret = spoe_recv_frame(appctx, frame,
1691 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001692 if (ret > 1) {
1693 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1694 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1695 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001696 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001697 trash.len = ret + 4;
1698 ret = spoe_handle_agenthello_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001699 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001700
Christopher Fauleta1cda022016-12-21 08:58:06 +01001701 switch (ret) {
1702 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001703 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001704 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1705 goto next;
1706
1707 case 1: /* retry later */
1708 goto stop;
1709
1710 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001711 /* HELLO handshake is finished, set the idle timeout and
1712 * add the applet in the list of running applets. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001713 agent->applets_idle++;
1714 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001715 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1716 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001717
1718 /* Update runtinme agent info */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001719 agent->frame_size = MIN(SPOE_APPCTX(appctx)->max_frame_size,
1720 agent->frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001721 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001722 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001723
Christopher Fauleta1cda022016-12-21 08:58:06 +01001724 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001725 /* Do not forget to remove processed frame from the output buffer */
1726 if (trash.len)
1727 bo_skip(si_oc(si), trash.len);
1728
1729 SPOE_APPCTX(appctx)->task->expire =
1730 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001731 return 0;
1732 stop:
1733 return 1;
1734 exit:
1735 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1736 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001737}
1738
Christopher Fauleta1cda022016-12-21 08:58:06 +01001739static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001740spoe_handle_sending_frame_appctx(struct appctx *appctx, struct spoe_context *ctx,
1741 int *skip)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001742{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001743 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
1744 char *frame, *buf;
1745 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001746
Christopher Faulet8ef75252017-02-20 22:56:03 +01001747 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1748 * length. */
1749 buf = trash.str; frame = buf+4;
1750 ret = spoe_prepare_hanotify_frame(appctx, ctx, frame,
1751 SPOE_APPCTX(appctx)->max_frame_size);
1752 if (ret > 1)
1753 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001754
Christopher Faulet8ef75252017-02-20 22:56:03 +01001755 switch (ret) {
1756 case -1: /* error */
1757 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1758 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001759
Christopher Faulet8ef75252017-02-20 22:56:03 +01001760 case 0: /* ignore */
1761 if (ctx == NULL)
1762 goto abort_frag_frame;
1763
1764 LIST_DEL(&ctx->list);
1765 LIST_INIT(&ctx->list);
1766 ctx->state = SPOE_CTX_ST_ERROR;
1767 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
1768 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1769 break;
1770
1771 case 1: /* retry */
1772 *skip = 1;
1773 break;
1774
1775 default:
1776 if (ctx == NULL)
1777 goto abort_frag_frame;
1778
1779 LIST_DEL(&ctx->list);
1780 LIST_INIT(&ctx->list);
1781 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) ||
1782 (ctx->frag_ctx.flags & SPOE_FRM_FL_FIN))
1783 goto no_frag_frame_sent;
1784 else {
1785 *skip = 1;
1786 goto frag_frame_sent;
1787 }
1788 }
1789 goto end;
1790
1791 frag_frame_sent:
1792 appctx->st0 = SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY;
1793 SPOE_APPCTX(appctx)->frag_ctx.ctx = ctx;
1794 SPOE_APPCTX(appctx)->frag_ctx.cursid = ctx->stream_id;
1795 SPOE_APPCTX(appctx)->frag_ctx.curfid = ctx->frame_id;
1796
1797 ctx->frag_ctx.spoe_appctx = SPOE_APPCTX(appctx);
1798 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
1799 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1800 goto end;
1801
1802 no_frag_frame_sent:
1803 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
1804 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1805 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1806 }
1807 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
1808 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1809 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1810 }
1811 else {
1812 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
1813 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1814 }
1815 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1816 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1817 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1818
1819 ctx->frag_ctx.spoe_appctx = NULL;
1820 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1821 goto end;
1822
1823 abort_frag_frame:
1824 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1825 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1826 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1827 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1828 goto end;
1829
1830 end:
1831 return ret;
1832}
1833
1834static int
1835spoe_handle_receiving_frame_appctx(struct appctx *appctx, int *skip)
1836{
1837 struct spoe_context *ctx = NULL;
1838 char *frame;
1839 int ret;
1840
1841 frame = trash.str; trash.len = 0;
1842 ret = spoe_recv_frame(appctx, frame,
1843 SPOE_APPCTX(appctx)->max_frame_size);
1844 if (ret > 1) {
1845 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1846 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1847 goto end;
1848 }
1849 trash.len = ret + 4;
1850 ret = spoe_handle_agentack_frame(appctx, &ctx, frame, ret);
1851 }
1852 switch (ret) {
1853 case -1: /* error */
1854 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1855 break;
1856
1857 case 0: /* ignore */
1858 break;
1859
1860 case 1: /* retry */
1861 *skip = 1;
1862 break;
1863
1864 default:
1865 LIST_DEL(&ctx->list);
1866 LIST_INIT(&ctx->list);
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001867
1868 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY &&
1869 ctx == SPOE_APPCTX(appctx)->frag_ctx.ctx) {
1870 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1871 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1872 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1873 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1874 }
1875 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1876 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1877
Christopher Faulet8ef75252017-02-20 22:56:03 +01001878 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1879 break;
1880 }
1881
1882 /* Do not forget to remove processed frame from the output buffer */
1883 if (trash.len)
1884 bo_skip(si_oc(appctx->owner), trash.len);
1885 end:
1886 return ret;
1887}
1888
1889static int
1890spoe_handle_processing_appctx(struct appctx *appctx)
1891{
1892 struct stream_interface *si = appctx->owner;
1893 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
1894 struct spoe_context *ctx = NULL;
1895 unsigned int fpa = 0;
1896 int ret, skip_sending = 0, skip_receiving = 0;
1897
1898 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1899 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
1900 goto exit;
1901 }
1902
1903 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1904 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
1905 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1906 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1907 goto next;
1908 }
1909
1910 process:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001911 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1912 " - process: fpa=%u/%u - skip_sending=%d - skip_receiving=%d"
1913 " - appctx-state=%s\n",
1914 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1915 __FUNCTION__, appctx, fpa, agent->max_fpa,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001916 skip_sending, skip_receiving,
1917 spoe_appctx_state_str[appctx->st0]);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001918
Christopher Fauleta1cda022016-12-21 08:58:06 +01001919 if (fpa > agent->max_fpa || (skip_sending && skip_receiving))
1920 goto stop;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001921 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001922 if (skip_receiving)
1923 goto stop;
1924 goto recv_frame;
1925 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001926 else if (skip_sending)
1927 goto recv_frame;
1928 else if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY) {
1929 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1930 goto send_frame;
1931 }
1932 else if (LIST_ISEMPTY(&agent->sending_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001933 skip_sending = 1;
1934 goto recv_frame;
1935 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001936 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
Christopher Faulet4596fb72017-01-11 14:05:19 +01001937
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001938 send_frame:
Christopher Faulet4596fb72017-01-11 14:05:19 +01001939 /* Transfer the buffer ownership to the SPOE appctx */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001940 if (ctx) {
1941 SPOE_APPCTX(appctx)->buffer = ctx->buffer;
1942 ctx->buffer = &buf_empty;
1943 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001944 ret = spoe_handle_sending_frame_appctx(appctx, ctx, &skip_sending);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001945 switch (ret) {
1946 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001947 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001948
Christopher Fauleta1cda022016-12-21 08:58:06 +01001949 case 0: /* ignore */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001950 spoe_release_buffer(&SPOE_APPCTX(appctx)->buffer,
1951 &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001952 agent->sending_rate++;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001953 fpa++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001954 break;
1955
Christopher Fauleta1cda022016-12-21 08:58:06 +01001956 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001957 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001958
Christopher Fauleta1cda022016-12-21 08:58:06 +01001959 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001960 spoe_release_buffer(&SPOE_APPCTX(appctx)->buffer,
1961 &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001962 agent->sending_rate++;
1963 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001964 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001965 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001966 if (fpa > agent->max_fpa)
1967 goto stop;
1968
1969 recv_frame:
1970 if (skip_receiving)
1971 goto process;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001972 ret = spoe_handle_receiving_frame_appctx(appctx, &skip_receiving);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001973 switch (ret) {
1974 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001975 goto next;
1976
1977 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001978 fpa++;
1979 break;
1980
1981 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001982 break;
1983
1984 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001985 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001986 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001987 }
1988 goto process;
1989
1990 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001991 SPOE_APPCTX(appctx)->task->expire =
1992 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001993 return 0;
1994 stop:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001995 if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001996 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001997 agent->applets_idle++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001998 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01001999 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
2000 LIST_DEL(&SPOE_APPCTX(appctx)->list);
2001 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002002 if (fpa)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002003 SPOE_APPCTX(appctx)->task->expire =
2004 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002005 }
2006 return 1;
2007
2008 exit:
2009 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2010 return 0;
2011}
2012
2013static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002014spoe_handle_disconnect_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002015{
2016 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002017 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002018 char *frame, *buf;
2019 int ret;
Christopher Fauletb067b062017-01-04 16:39:11 +01002020
Christopher Fauleta1cda022016-12-21 08:58:06 +01002021 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
2022 goto exit;
2023
2024 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
2025 goto exit;
2026
Christopher Faulet8ef75252017-02-20 22:56:03 +01002027 /* 4 bytes are reserved at the beginning of <buf> to store the frame
2028 * length. */
2029 buf = trash.str; frame = buf+4;
2030 ret = spoe_prepare_hadiscon_frame(appctx, frame,
2031 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002032 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002033 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002034
2035 switch (ret) {
2036 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002037 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002038 goto exit;
2039
2040 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002041 goto stop;
2042
2043 default:
2044 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2045 " - disconnected by HAProxy (%d): %s\n",
2046 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002047 __FUNCTION__, appctx,
2048 SPOE_APPCTX(appctx)->status_code,
2049 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002050
2051 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
2052 goto next;
2053 }
2054
2055 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002056 SPOE_APPCTX(appctx)->task->expire =
2057 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002058 return 0;
2059 stop:
2060 return 1;
2061 exit:
2062 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2063 return 0;
2064}
2065
2066static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002067spoe_handle_disconnecting_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002068{
2069 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002070 char *frame;
2071 int ret;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002072
Christopher Fauletb067b062017-01-04 16:39:11 +01002073 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002074 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002075 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002076 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002077
Christopher Fauletb067b062017-01-04 16:39:11 +01002078 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002079 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002080 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002081 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002082
Christopher Faulet8ef75252017-02-20 22:56:03 +01002083 frame = trash.str; trash.len = 0;
2084 ret = spoe_recv_frame(appctx, frame,
2085 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002086 if (ret > 1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002087 trash.len = ret + 4;
2088 ret = spoe_handle_agentdiscon_frame(appctx, frame, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002089 }
2090
2091 switch (ret) {
2092 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002093 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2094 " - error on frame (%s)\n",
2095 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002096 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01002097 __FUNCTION__, appctx,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002098 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002099 goto exit;
2100
2101 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002102 goto next;
2103
2104 case 1: /* retry */
2105 goto stop;
2106
2107 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01002108 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002109 " - disconnected by peer (%d): %.*s\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002110 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002111 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002112 __FUNCTION__, appctx, SPOE_APPCTX(appctx)->status_code,
2113 SPOE_APPCTX(appctx)->rlen, SPOE_APPCTX(appctx)->reason);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002114 goto exit;
2115 }
2116
2117 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002118 /* Do not forget to remove processed frame from the output buffer */
2119 if (trash.len)
2120 bo_skip(si_oc(appctx->owner), trash.len);
2121
Christopher Fauleta1cda022016-12-21 08:58:06 +01002122 return 0;
2123 stop:
2124 return 1;
2125 exit:
2126 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2127 return 0;
2128}
2129
2130/* I/O Handler processing messages exchanged with the agent */
2131static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002132spoe_handle_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002133{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002134 struct stream_interface *si = appctx->owner;
2135 struct spoe_agent *agent;
2136
2137 if (SPOE_APPCTX(appctx) == NULL)
2138 return;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002139
Christopher Faulet8ef75252017-02-20 22:56:03 +01002140 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
2141 agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletb067b062017-01-04 16:39:11 +01002142
Christopher Fauleta1cda022016-12-21 08:58:06 +01002143 switchstate:
2144 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2145 " - appctx-state=%s\n",
2146 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2147 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
2148
2149 switch (appctx->st0) {
2150 case SPOE_APPCTX_ST_CONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002151 if (spoe_handle_connect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002152 goto out;
2153 goto switchstate;
2154
2155 case SPOE_APPCTX_ST_CONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002156 if (spoe_handle_connecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002157 goto out;
2158 goto switchstate;
2159
2160 case SPOE_APPCTX_ST_IDLE:
2161 if (stopping &&
2162 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01002163 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002164 SPOE_APPCTX(appctx)->task->expire =
2165 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002166 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002167 goto switchstate;
2168 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002169 agent->applets_idle--;
2170 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2171 /* fall through */
2172
2173 case SPOE_APPCTX_ST_PROCESSING:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002174 case SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY:
2175 case SPOE_APPCTX_ST_WAITING_SYNC_ACK:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002176 if (spoe_handle_processing_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002177 goto out;
2178 goto switchstate;
2179
2180 case SPOE_APPCTX_ST_DISCONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002181 if (spoe_handle_disconnect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002182 goto out;
2183 goto switchstate;
2184
2185 case SPOE_APPCTX_ST_DISCONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002186 if (spoe_handle_disconnecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002187 goto out;
2188 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002189
2190 case SPOE_APPCTX_ST_EXIT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002191 appctx->st0 = SPOE_APPCTX_ST_END;
2192 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
2193
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002194 si_shutw(si);
2195 si_shutr(si);
2196 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002197 /* fall through */
2198
2199 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002200 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002201 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002202 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002203 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
2204 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002205 si_oc(si)->flags |= CF_READ_DONTWAIT;
2206 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002207}
2208
2209struct applet spoe_applet = {
2210 .obj_type = OBJ_TYPE_APPLET,
2211 .name = "<SPOE>", /* used for logging */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002212 .fct = spoe_handle_appctx,
2213 .release = spoe_release_appctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002214};
2215
2216/* Create a SPOE applet. On success, the created applet is returned, else
2217 * NULL. */
2218static struct appctx *
Christopher Faulet8ef75252017-02-20 22:56:03 +01002219spoe_create_appctx(struct spoe_config *conf)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002220{
2221 struct appctx *appctx;
2222 struct session *sess;
2223 struct task *task;
2224 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002225
2226 if ((appctx = appctx_new(&spoe_applet)) == NULL)
2227 goto out_error;
2228
Christopher Faulet42bfa462017-01-04 14:14:19 +01002229 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
2230 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002231 goto out_free_appctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002232 memset(appctx->ctx.spoe.ptr, 0, pool2_spoe_appctx->size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002233
Christopher Faulet42bfa462017-01-04 14:14:19 +01002234 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
2235 if ((SPOE_APPCTX(appctx)->task = task_new()) == NULL)
2236 goto out_free_spoe_appctx;
2237
2238 SPOE_APPCTX(appctx)->owner = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002239 SPOE_APPCTX(appctx)->task->process = spoe_process_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002240 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
2241 SPOE_APPCTX(appctx)->task->context = appctx;
2242 SPOE_APPCTX(appctx)->agent = conf->agent;
2243 SPOE_APPCTX(appctx)->version = 0;
2244 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
2245 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01002246 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet4596fb72017-01-11 14:05:19 +01002247 SPOE_APPCTX(appctx)->buffer = &buf_empty;
2248
2249 LIST_INIT(&SPOE_APPCTX(appctx)->buffer_wait.list);
2250 SPOE_APPCTX(appctx)->buffer_wait.target = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002251 SPOE_APPCTX(appctx)->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002252
2253 LIST_INIT(&SPOE_APPCTX(appctx)->list);
2254 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002255
Willy Tarreau5820a362016-12-22 15:59:02 +01002256 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002257 if (!sess)
2258 goto out_free_spoe;
2259
2260 if ((task = task_new()) == NULL)
2261 goto out_free_sess;
2262
2263 if ((strm = stream_new(sess, task, &appctx->obj_type)) == NULL)
2264 goto out_free_task;
2265
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002266 stream_set_backend(strm, conf->agent->b.be);
2267
2268 /* applet is waiting for data */
2269 si_applet_cant_get(&strm->si[0]);
2270 appctx_wakeup(appctx);
2271
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002272 strm->do_log = NULL;
2273 strm->res.flags |= CF_READ_DONTWAIT;
2274
2275 conf->agent_fe.feconn++;
2276 jobs++;
2277 totalconn++;
2278
Christopher Faulet42bfa462017-01-04 14:14:19 +01002279 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
2280 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002281 conf->agent->applets_act++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002282 return appctx;
2283
2284 /* Error unrolling */
2285 out_free_task:
2286 task_free(task);
2287 out_free_sess:
2288 session_free(sess);
2289 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002290 task_free(SPOE_APPCTX(appctx)->task);
2291 out_free_spoe_appctx:
2292 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002293 out_free_appctx:
2294 appctx_free(appctx);
2295 out_error:
2296 return NULL;
2297}
2298
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002299static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002300spoe_queue_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002301{
2302 struct spoe_config *conf = FLT_CONF(ctx->filter);
2303 struct spoe_agent *agent = conf->agent;
2304 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002305 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002306 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002307
Christopher Fauleta1cda022016-12-21 08:58:06 +01002308 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002309
Christopher Fauleta1cda022016-12-21 08:58:06 +01002310 /* Check if we need to create a new SPOE applet or not. */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002311 if (agent->applets_act >= min_applets &&
2312 agent->applets_idle &&
2313 agent->sending_rate)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002314 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002315
2316 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002317 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002318 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2319 ctx->strm);
2320
Christopher Fauleta1cda022016-12-21 08:58:06 +01002321 /* Do not try to create a new applet if there is no server up for the
2322 * agent's backend. */
2323 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2324 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2325 " - cannot create SPOE appctx: no server up\n",
2326 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2327 __FUNCTION__, ctx->strm);
2328 goto end;
2329 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002330
Christopher Fauleta1cda022016-12-21 08:58:06 +01002331 /* Do not try to create a new applet if we have reached the maximum of
2332 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002333 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002334 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2335 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2336 " - cannot create SPOE appctx: max CPS reached\n",
2337 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2338 __FUNCTION__, ctx->strm);
2339 goto end;
2340 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002341 }
2342
Christopher Faulet8ef75252017-02-20 22:56:03 +01002343 appctx = spoe_create_appctx(conf);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002344 if (appctx == NULL) {
2345 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2346 " - failed to create SPOE appctx\n",
2347 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2348 __FUNCTION__, ctx->strm);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002349 send_log(ctx->strm->be, LOG_EMERG,
2350 "SPOE: [%s] failed to create SPOE applet\n",
2351 agent->id);
2352
Christopher Fauleta1cda022016-12-21 08:58:06 +01002353 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002354 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002355 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002356 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002357
Christopher Fauleta1cda022016-12-21 08:58:06 +01002358 /* Increase the per-process number of cumulated connections */
2359 if (agent->cps_max > 0)
2360 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002361
Christopher Fauleta1cda022016-12-21 08:58:06 +01002362 end:
2363 /* The only reason to return an error is when there is no applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002364 if (LIST_ISEMPTY(&agent->applets)) {
2365 ctx->status_code = SPOE_CTX_ERR_RES;
2366 return -1;
2367 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002368
Christopher Fauleta1cda022016-12-21 08:58:06 +01002369 /* Add the SPOE context in the sending queue and update all running
2370 * info */
2371 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2372 if (agent->sending_rate)
2373 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002374
2375 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002376 " - Add stream in sending queue"
2377 " - applets_act=%u - applets_idle=%u - sending_rate=%u\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002378 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002379 ctx->strm, agent->applets_act, agent->applets_idle,
2380 agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002381
Christopher Fauleta1cda022016-12-21 08:58:06 +01002382 /* Finally try to wakeup the first IDLE applet found and move it at the
2383 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002384 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2385 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002386 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002387 spoe_wakeup_appctx(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002388 LIST_DEL(&spoe_appctx->list);
2389 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002390 break;
2391 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002392 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002393 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002394}
2395
2396/***************************************************************************
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002397 * Functions that encode SPOE messages
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002398 **************************************************************************/
Christopher Faulet8ef75252017-02-20 22:56:03 +01002399/* Encode SPOE messages for a specific event. Info in <ctx->frag_ctx>, if any,
2400 * are used to handle fragmented content. On success it returns 1. If an error
2401 * occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002402static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002403spoe_encode_messages(struct stream *s, struct spoe_context *ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002404 struct list *messages, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002405{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002406 struct spoe_config *conf = FLT_CONF(ctx->filter);
2407 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002408 struct spoe_message *msg;
2409 struct sample *smp;
2410 struct spoe_arg *arg;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002411 char *p, *end;
2412 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002413
Christopher Faulet8ef75252017-02-20 22:56:03 +01002414 p = ctx->buffer->p;
2415 end = p + agent->frame_size - FRAME_HDR_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002416
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002417 /* Resume encoding of a SPOE message */
2418 if (ctx->frag_ctx.curmsg != NULL) {
2419 msg = ctx->frag_ctx.curmsg;
2420 goto encode_message;
2421 }
2422
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002423 /* Loop on messages */
2424 list_for_each_entry(msg, messages, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002425 ctx->frag_ctx.curmsg = msg;
2426 ctx->frag_ctx.curarg = NULL;
2427 ctx->frag_ctx.curoff = UINT_MAX;
2428
2429 encode_message:
2430 /* Resume encoding of a SPOE argument */
2431 if (ctx->frag_ctx.curarg != NULL) {
2432 arg = ctx->frag_ctx.curarg;
2433 goto encode_argument;
2434 }
2435
2436 if (ctx->frag_ctx.curoff != UINT_MAX)
2437 goto encode_msg_payload;
2438
Christopher Faulet8ef75252017-02-20 22:56:03 +01002439 /* Check if there is enough space for the message name and the
2440 * number of arguments. It implies <msg->id_len> is encoded on 2
2441 * bytes, at most (< 2288). */
2442 if (p + 2 + msg->id_len + 1 > end)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002443 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002444
Christopher Faulet8ef75252017-02-20 22:56:03 +01002445 /* Encode the message name */
2446 if (spoe_encode_buffer(msg->id, msg->id_len, &p, end) == -1)
2447 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002448
Christopher Faulet8ef75252017-02-20 22:56:03 +01002449 /* Set the number of arguments for this message */
2450 *p++ = msg->nargs;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002451
2452 ctx->frag_ctx.curoff = 0;
2453 encode_msg_payload:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002454
2455 /* Loop on arguments */
2456 list_for_each_entry(arg, &msg->args, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002457 ctx->frag_ctx.curarg = arg;
2458 ctx->frag_ctx.curoff = UINT_MAX;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002459
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002460 encode_argument:
2461 if (ctx->frag_ctx.curoff != UINT_MAX)
2462 goto encode_arg_value;
2463
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002464 /* Encode the arguement name as a string. It can by NULL */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002465 if (spoe_encode_buffer(arg->name, arg->name_len, &p, end) == -1)
2466 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002467
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002468 ctx->frag_ctx.curoff = 0;
2469 encode_arg_value:
2470
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002471 /* Fetch the arguement value */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002472 smp = sample_process(s->be, s->sess, s,
2473 dir|SMP_OPT_FINAL, arg->expr, NULL);
2474 ret = spoe_encode_data(smp, &ctx->frag_ctx.curoff, &p, end);
2475 if (ret == -1 || ctx->frag_ctx.curoff)
2476 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002477 }
2478 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002479
2480 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002481 " - encode %s messages - spoe_appctx=%p"
2482 "- max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002483 (int)now.tv_sec, (int)now.tv_usec,
2484 agent->id, __FUNCTION__, s,
2485 ((ctx->flags & SPOE_CTX_FL_FRAGMENTED) ? "last fragment of" : "unfragmented"),
Christopher Faulet8ef75252017-02-20 22:56:03 +01002486 ctx->frag_ctx.spoe_appctx, (agent->frame_size - FRAME_HDR_SIZE),
2487 p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002488
Christopher Faulet8ef75252017-02-20 22:56:03 +01002489 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002490 ctx->frag_ctx.curmsg = NULL;
2491 ctx->frag_ctx.curarg = NULL;
2492 ctx->frag_ctx.curoff = 0;
2493 ctx->frag_ctx.flags = SPOE_FRM_FL_FIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002494 return 1;
2495
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002496 too_big:
2497 // FIXME: if fragmentation not supported =>
2498 // ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
2499 // return -1;
2500
2501 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002502 " - encode fragmented messages - spoe_appctx=%p"
2503 " - curmsg=%p - curarg=%p - curoff=%u"
2504 " - max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002505 (int)now.tv_sec, (int)now.tv_usec,
2506 agent->id, __FUNCTION__, s, ctx->frag_ctx.spoe_appctx,
2507 ctx->frag_ctx.curmsg, ctx->frag_ctx.curarg, ctx->frag_ctx.curoff,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002508 (agent->frame_size - FRAME_HDR_SIZE), p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002509
Christopher Faulet8ef75252017-02-20 22:56:03 +01002510 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002511 ctx->flags |= SPOE_CTX_FL_FRAGMENTED;
2512 ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN;
2513 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002514}
2515
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002516
2517/***************************************************************************
2518 * Functions that handle SPOE actions
2519 **************************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002520/* Helper function to set a variable */
2521static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002522spoe_set_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002523 struct sample *smp)
2524{
2525 struct spoe_config *conf = FLT_CONF(ctx->filter);
2526 struct spoe_agent *agent = conf->agent;
2527 char varname[64];
2528
2529 memset(varname, 0, sizeof(varname));
2530 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2531 scope, agent->var_pfx, len, name);
2532 vars_set_by_name_ifexist(varname, len, smp);
2533}
2534
2535/* Helper function to unset a variable */
2536static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002537spoe_unset_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002538 struct sample *smp)
2539{
2540 struct spoe_config *conf = FLT_CONF(ctx->filter);
2541 struct spoe_agent *agent = conf->agent;
2542 char varname[64];
2543
2544 memset(varname, 0, sizeof(varname));
2545 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2546 scope, agent->var_pfx, len, name);
2547 vars_unset_by_name_ifexist(varname, len, smp);
2548}
2549
2550
Christopher Faulet8ef75252017-02-20 22:56:03 +01002551static inline int
2552spoe_decode_action_set_var(struct stream *s, struct spoe_context *ctx,
2553 char **buf, char *end, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002554{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002555 char *str, *scope, *p = *buf;
2556 struct sample smp;
2557 uint64_t sz;
2558 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002559
Christopher Faulet8ef75252017-02-20 22:56:03 +01002560 if (p + 2 >= end)
2561 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002562
Christopher Faulet8ef75252017-02-20 22:56:03 +01002563 /* SET-VAR requires 3 arguments */
2564 if (*p++ != 3)
2565 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002566
Christopher Faulet8ef75252017-02-20 22:56:03 +01002567 switch (*p++) {
2568 case SPOE_SCOPE_PROC: scope = "proc"; break;
2569 case SPOE_SCOPE_SESS: scope = "sess"; break;
2570 case SPOE_SCOPE_TXN : scope = "txn"; break;
2571 case SPOE_SCOPE_REQ : scope = "req"; break;
2572 case SPOE_SCOPE_RES : scope = "res"; break;
2573 default: goto skip;
2574 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002575
Christopher Faulet8ef75252017-02-20 22:56:03 +01002576 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2577 goto skip;
2578 memset(&smp, 0, sizeof(smp));
2579 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002580
Christopher Faulet8ef75252017-02-20 22:56:03 +01002581 if (spoe_decode_data(&p, end, &smp) == -1)
2582 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002583
Christopher Faulet8ef75252017-02-20 22:56:03 +01002584 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2585 " - set-var '%s.%s.%.*s'\n",
2586 (int)now.tv_sec, (int)now.tv_usec,
2587 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2588 __FUNCTION__, s, scope,
2589 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2590 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002591
Christopher Faulet8ef75252017-02-20 22:56:03 +01002592 spoe_set_var(ctx, scope, str, sz, &smp);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002593
Christopher Faulet8ef75252017-02-20 22:56:03 +01002594 ret = (p - *buf);
2595 *buf = p;
2596 return ret;
2597 skip:
2598 return 0;
2599}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002600
Christopher Faulet8ef75252017-02-20 22:56:03 +01002601static inline int
2602spoe_decode_action_unset_var(struct stream *s, struct spoe_context *ctx,
2603 char **buf, char *end, int dir)
2604{
2605 char *str, *scope, *p = *buf;
2606 struct sample smp;
2607 uint64_t sz;
2608 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002609
Christopher Faulet8ef75252017-02-20 22:56:03 +01002610 if (p + 2 >= end)
2611 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002612
Christopher Faulet8ef75252017-02-20 22:56:03 +01002613 /* UNSET-VAR requires 2 arguments */
2614 if (*p++ != 2)
2615 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002616
Christopher Faulet8ef75252017-02-20 22:56:03 +01002617 switch (*p++) {
2618 case SPOE_SCOPE_PROC: scope = "proc"; break;
2619 case SPOE_SCOPE_SESS: scope = "sess"; break;
2620 case SPOE_SCOPE_TXN : scope = "txn"; break;
2621 case SPOE_SCOPE_REQ : scope = "req"; break;
2622 case SPOE_SCOPE_RES : scope = "res"; break;
2623 default: goto skip;
2624 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002625
Christopher Faulet8ef75252017-02-20 22:56:03 +01002626 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2627 goto skip;
2628 memset(&smp, 0, sizeof(smp));
2629 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002630
Christopher Faulet8ef75252017-02-20 22:56:03 +01002631 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2632 " - unset-var '%s.%s.%.*s'\n",
2633 (int)now.tv_sec, (int)now.tv_usec,
2634 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2635 __FUNCTION__, s, scope,
2636 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2637 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002638
Christopher Faulet8ef75252017-02-20 22:56:03 +01002639 spoe_unset_var(ctx, scope, str, sz, &smp);
2640
2641 ret = (p - *buf);
2642 *buf = p;
2643 return ret;
2644 skip:
2645 return 0;
2646}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002647
Christopher Faulet8ef75252017-02-20 22:56:03 +01002648/* Process SPOE actions for a specific event. It returns 1 on success. If an
2649 * error occurred, 0 is returned. */
2650static int
2651spoe_process_actions(struct stream *s, struct spoe_context *ctx,
2652 enum spoe_event ev, int dir)
2653{
2654 char *p, *end;
2655 int ret;
2656
2657 p = ctx->buffer->p;
2658 end = p + ctx->buffer->i;
2659
2660 while (p < end) {
2661 enum spoe_action_type type;
2662
2663 type = *p++;
2664 switch (type) {
2665 case SPOE_ACT_T_SET_VAR:
2666 ret = spoe_decode_action_set_var(s, ctx, &p, end, dir);
2667 if (!ret)
2668 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002669 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002670
Christopher Faulet8ef75252017-02-20 22:56:03 +01002671 case SPOE_ACT_T_UNSET_VAR:
2672 ret = spoe_decode_action_unset_var(s, ctx, &p, end, dir);
2673 if (!ret)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002674 goto skip;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002675 break;
2676
2677 default:
2678 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002679 }
2680 }
2681
2682 return 1;
2683 skip:
2684 return 0;
2685}
2686
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002687/***************************************************************************
2688 * Functions that process SPOE events
2689 **************************************************************************/
2690static inline int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002691spoe_start_event_processing(struct spoe_context *ctx, int dir)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002692{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002693 /* If a process is already started for this SPOE context, retry
2694 * later. */
2695 if (ctx->flags & SPOE_CTX_FL_PROCESS)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002696 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002697
2698 /* Set the right flag to prevent request and response processing
2699 * in same time. */
2700 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2701 ? SPOE_CTX_FL_REQ_PROCESS
2702 : SPOE_CTX_FL_RSP_PROCESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002703 return 1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002704}
2705
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002706static inline void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002707spoe_stop_event_processing(struct spoe_context *ctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002708{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002709 struct spoe_appctx *sa = ctx->frag_ctx.spoe_appctx;
2710
2711 if (sa) {
2712 sa->frag_ctx.ctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002713 spoe_wakeup_appctx(sa->owner);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002714 }
2715
Christopher Fauleta1cda022016-12-21 08:58:06 +01002716 /* Reset the flag to allow next processing */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002717 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002718
Christopher Fauletb067b062017-01-04 16:39:11 +01002719 ctx->status_code = 0;
2720
Christopher Fauleta1cda022016-12-21 08:58:06 +01002721 /* Reset processing timer */
2722 ctx->process_exp = TICK_ETERNITY;
2723
Christopher Faulet8ef75252017-02-20 22:56:03 +01002724 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002725
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002726 ctx->frag_ctx.spoe_appctx = NULL;
2727 ctx->frag_ctx.curmsg = NULL;
2728 ctx->frag_ctx.curarg = NULL;
2729 ctx->frag_ctx.curoff = 0;
2730 ctx->frag_ctx.flags = 0;
2731
Christopher Fauleta1cda022016-12-21 08:58:06 +01002732 if (!LIST_ISEMPTY(&ctx->list)) {
2733 LIST_DEL(&ctx->list);
2734 LIST_INIT(&ctx->list);
2735 }
2736}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002737
2738/* Process a SPOE event. First, this functions will process messages attached to
2739 * this event and send them to an agent in a NOTIFY frame. Then, it will wait a
2740 * ACK frame to process corresponding actions. During all the processing, it
2741 * returns 0 and it returns 1 when the processing is finished. If an error
2742 * occurred, -1 is returned. */
2743static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002744spoe_process_event(struct stream *s, struct spoe_context *ctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002745 enum spoe_event ev)
2746{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002747 struct spoe_config *conf = FLT_CONF(ctx->filter);
2748 struct spoe_agent *agent = conf->agent;
2749 int dir, ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002750
2751 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2752 " - ctx-state=%s - event=%s\n",
2753 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletf7a30922016-11-10 15:04:51 +01002754 agent->id, __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002755 spoe_event_str[ev]);
2756
2757 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2758
2759 if (LIST_ISEMPTY(&(ctx->messages[ev])))
2760 goto out;
2761
2762 if (ctx->state == SPOE_CTX_ST_ERROR)
2763 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002764
2765 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2766 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2767 " - failed to process event '%s': timeout\n",
2768 (int)now.tv_sec, (int)now.tv_usec,
2769 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
Christopher Fauletb067b062017-01-04 16:39:11 +01002770 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002771 goto error;
2772 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002773
2774 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002775 if (agent->eps_max > 0) {
2776 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
2777 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2778 " - skip event '%s': max EPS reached\n",
2779 (int)now.tv_sec, (int)now.tv_usec,
2780 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
2781 goto skip;
2782 }
2783 }
2784
Christopher Fauletf7a30922016-11-10 15:04:51 +01002785 if (!tick_isset(ctx->process_exp)) {
2786 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2787 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2788 ctx->process_exp);
2789 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01002790 ret = spoe_start_event_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002791 if (!ret)
2792 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002793
Christopher Faulet8ef75252017-02-20 22:56:03 +01002794 if (spoe_queue_context(ctx) < 0)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002795 goto error;
2796
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002797 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002798 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002799 }
2800
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002801 if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002802 if (!spoe_acquire_buffer(&ctx->buffer, &ctx->buffer_wait))
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002803 goto out;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002804 ret = spoe_encode_messages(s, ctx, &(ctx->messages[ev]), dir);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002805 if (ret < 0)
2806 goto error;
2807 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2808 }
2809
2810 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
2811 if (ctx->frag_ctx.spoe_appctx)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002812 spoe_wakeup_appctx(ctx->frag_ctx.spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002813 ret = 0;
2814 goto out;
2815 }
2816
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002817 if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
2818 ret = 0;
2819 goto out;
2820 }
2821
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002822 if (ctx->state == SPOE_CTX_ST_DONE) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002823 spoe_process_actions(s, ctx, ev, dir);
2824 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002825 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002826 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002827 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002828 }
2829
2830 out:
2831 return ret;
2832
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002833 error:
Christopher Faulet48026722016-11-16 15:01:12 +01002834 if (agent->eps_max > 0)
2835 update_freq_ctr(&agent->err_per_sec, 1);
2836
Christopher Faulet985532d2016-11-16 15:36:19 +01002837 if (agent->var_on_error) {
2838 struct sample smp;
2839
2840 memset(&smp, 0, sizeof(smp));
2841 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb067b062017-01-04 16:39:11 +01002842 smp.data.u.sint = ctx->status_code;
Christopher Faulet985532d2016-11-16 15:36:19 +01002843 smp.data.type = SMP_T_BOOL;
2844
Christopher Faulet8ef75252017-02-20 22:56:03 +01002845 spoe_set_var(ctx, "txn", agent->var_on_error,
Christopher Faulet985532d2016-11-16 15:36:19 +01002846 strlen(agent->var_on_error), &smp);
2847 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002848 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2849 " - failed to create process event '%s': code=%u\n",
2850 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2851 __FUNCTION__, ctx->strm, spoe_event_str[ev],
2852 ctx->status_code);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002853 send_log(ctx->strm->be, LOG_WARNING,
2854 "SPOE: [%s] failed to process event '%s': code=%u\n",
2855 agent->id, spoe_event_str[ev], ctx->status_code);
Christopher Faulet985532d2016-11-16 15:36:19 +01002856
Christopher Fauletea62c2a2016-11-14 10:54:21 +01002857 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2858 ? SPOE_CTX_ST_READY
Christopher Fauletb067b062017-01-04 16:39:11 +01002859 : SPOE_CTX_ST_NONE);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002860 ret = 1;
2861 goto end;
2862
2863 skip:
2864 ctx->state = SPOE_CTX_ST_READY;
2865 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002866
Christopher Fauleta1cda022016-12-21 08:58:06 +01002867 end:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002868 spoe_stop_event_processing(ctx);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002869 return ret;
2870}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002871
2872/***************************************************************************
2873 * Functions that create/destroy SPOE contexts
2874 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002875static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002876spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002877{
Christopher Faulet4596fb72017-01-11 14:05:19 +01002878 if (*buf != &buf_empty)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002879 return 1;
2880
Christopher Faulet4596fb72017-01-11 14:05:19 +01002881 if (!LIST_ISEMPTY(&buffer_wait->list)) {
2882 LIST_DEL(&buffer_wait->list);
2883 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002884 }
2885
Christopher Faulet4596fb72017-01-11 14:05:19 +01002886 if (b_alloc_margin(buf, global.tune.reserved_bufs))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002887 return 1;
2888
Christopher Faulet4596fb72017-01-11 14:05:19 +01002889 LIST_ADDQ(&buffer_wq, &buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002890 return 0;
2891}
2892
2893static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002894spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002895{
Christopher Faulet4596fb72017-01-11 14:05:19 +01002896 if (!LIST_ISEMPTY(&buffer_wait->list)) {
2897 LIST_DEL(&buffer_wait->list);
2898 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002899 }
2900
2901 /* Release the buffer if needed */
Christopher Faulet4596fb72017-01-11 14:05:19 +01002902 if (*buf != &buf_empty) {
2903 b_free(buf);
2904 offer_buffers(buffer_wait->target,
2905 tasks_run_queue + applets_active_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002906 }
2907}
2908
Christopher Faulet4596fb72017-01-11 14:05:19 +01002909static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002910spoe_wakeup_context(struct spoe_context *ctx)
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002911{
2912 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2913 return 1;
2914}
2915
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002916static struct spoe_context *
Christopher Faulet8ef75252017-02-20 22:56:03 +01002917spoe_create_context(struct filter *filter)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002918{
2919 struct spoe_config *conf = FLT_CONF(filter);
2920 struct spoe_context *ctx;
2921
2922 ctx = pool_alloc_dirty(pool2_spoe_ctx);
2923 if (ctx == NULL) {
2924 return NULL;
2925 }
2926 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01002927 ctx->filter = filter;
2928 ctx->state = SPOE_CTX_ST_NONE;
2929 ctx->status_code = SPOE_CTX_ERR_NONE;
2930 ctx->flags = 0;
2931 ctx->messages = conf->agent->messages;
2932 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002933 LIST_INIT(&ctx->buffer_wait.list);
2934 ctx->buffer_wait.target = ctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002935 ctx->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002936 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002937
Christopher Fauletf7a30922016-11-10 15:04:51 +01002938 ctx->stream_id = 0;
2939 ctx->frame_id = 1;
2940 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002941
2942 return ctx;
2943}
2944
2945static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002946spoe_destroy_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002947{
2948 if (!ctx)
2949 return;
2950
Christopher Faulet8ef75252017-02-20 22:56:03 +01002951 spoe_stop_event_processing(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002952 pool_free2(pool2_spoe_ctx, ctx);
2953}
2954
2955static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002956spoe_reset_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002957{
2958 ctx->state = SPOE_CTX_ST_READY;
2959 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
2960}
2961
2962
2963/***************************************************************************
2964 * Hooks that manage the filter lifecycle (init/check/deinit)
2965 **************************************************************************/
2966/* Signal handler: Do a soft stop, wakeup SPOE applet */
2967static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002968spoe_sig_stop(struct sig_handler *sh)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002969{
2970 struct proxy *p;
2971
2972 p = proxy;
2973 while (p) {
2974 struct flt_conf *fconf;
2975
2976 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01002977 struct spoe_config *conf;
2978 struct spoe_agent *agent;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002979 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002980
Christopher Faulet3b386a32017-02-23 10:17:15 +01002981 if (fconf->id != spoe_filter_id)
2982 continue;
2983
2984 conf = fconf->conf;
2985 agent = conf->agent;
2986
Christopher Faulet42bfa462017-01-04 14:14:19 +01002987 list_for_each_entry(spoe_appctx, &agent->applets, list) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002988 spoe_wakeup_appctx(spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002989 }
2990 }
2991 p = p->next;
2992 }
2993}
2994
2995
2996/* Initialize the SPOE filter. Returns -1 on error, else 0. */
2997static int
2998spoe_init(struct proxy *px, struct flt_conf *fconf)
2999{
3000 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003001
3002 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
3003 init_new_proxy(&conf->agent_fe);
3004 conf->agent_fe.parent = conf->agent;
3005 conf->agent_fe.last_change = now.tv_sec;
3006 conf->agent_fe.id = conf->agent->id;
3007 conf->agent_fe.cap = PR_CAP_FE;
3008 conf->agent_fe.mode = PR_MODE_TCP;
3009 conf->agent_fe.maxconn = 0;
3010 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
3011 conf->agent_fe.conn_retries = CONN_RETRIES;
3012 conf->agent_fe.accept = frontend_accept;
3013 conf->agent_fe.srv = NULL;
3014 conf->agent_fe.timeout.client = TICK_ETERNITY;
3015 conf->agent_fe.default_target = &spoe_applet.obj_type;
3016 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
3017
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003018 if (!sighandler_registered) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003019 signal_register_fct(0, spoe_sig_stop, 0);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003020 sighandler_registered = 1;
3021 }
3022
3023 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003024}
3025
3026/* Free ressources allocated by the SPOE filter. */
3027static void
3028spoe_deinit(struct proxy *px, struct flt_conf *fconf)
3029{
3030 struct spoe_config *conf = fconf->conf;
3031
3032 if (conf) {
3033 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003034
Christopher Faulet8ef75252017-02-20 22:56:03 +01003035 spoe_release_agent(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003036 free(conf);
3037 }
3038 fconf->conf = NULL;
3039}
3040
3041/* Check configuration of a SPOE filter for a specified proxy.
3042 * Return 1 on error, else 0. */
3043static int
3044spoe_check(struct proxy *px, struct flt_conf *fconf)
3045{
3046 struct spoe_config *conf = fconf->conf;
3047 struct proxy *target;
3048
3049 target = proxy_be_by_name(conf->agent->b.name);
3050 if (target == NULL) {
3051 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
3052 " declared at %s:%d.\n",
3053 px->id, conf->agent->b.name, conf->agent->id,
3054 conf->agent->conf.file, conf->agent->conf.line);
3055 return 1;
3056 }
3057 if (target->mode != PR_MODE_TCP) {
3058 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
3059 " at %s:%d does not support HTTP mode.\n",
3060 px->id, target->id, conf->agent->id,
3061 conf->agent->conf.file, conf->agent->conf.line);
3062 return 1;
3063 }
3064
3065 free(conf->agent->b.name);
3066 conf->agent->b.name = NULL;
3067 conf->agent->b.be = target;
3068 return 0;
3069}
3070
3071/**************************************************************************
3072 * Hooks attached to a stream
3073 *************************************************************************/
3074/* Called when a filter instance is created and attach to a stream. It creates
3075 * the context that will be used to process this stream. */
3076static int
3077spoe_start(struct stream *s, struct filter *filter)
3078{
Christopher Faulet72bcc472017-01-04 16:39:41 +01003079 struct spoe_config *conf = FLT_CONF(filter);
3080 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003081 struct spoe_context *ctx;
3082
3083 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01003084 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003085 __FUNCTION__, s);
3086
Christopher Faulet8ef75252017-02-20 22:56:03 +01003087 ctx = spoe_create_context(filter);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003088 if (ctx == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01003089 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3090 " - failed to create SPOE context\n",
3091 (int)now.tv_sec, (int)now.tv_usec, agent->id,
3092 __FUNCTION__, ctx->strm);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003093 send_log(s->be, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01003094 "SPOE: [%s] failed to create SPOE context\n",
3095 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003096 return 0;
3097 }
3098
3099 ctx->strm = s;
3100 ctx->state = SPOE_CTX_ST_READY;
3101 filter->ctx = ctx;
3102
3103 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_FE]))
3104 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
3105
3106 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_BE]))
3107 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
3108
3109 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_RSP]))
3110 filter->pre_analyzers |= AN_RES_INSPECT;
3111
3112 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_FE]))
3113 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
3114
3115 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_BE]))
3116 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
3117
3118 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_RSP]))
3119 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
3120
3121 return 1;
3122}
3123
3124/* Called when a filter instance is detached from a stream. It release the
3125 * attached SPOE context. */
3126static void
3127spoe_stop(struct stream *s, struct filter *filter)
3128{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003129 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
3130 (int)now.tv_sec, (int)now.tv_usec,
3131 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3132 __FUNCTION__, s);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003133 spoe_destroy_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003134}
3135
Christopher Fauletf7a30922016-11-10 15:04:51 +01003136
3137/*
3138 * Called when the stream is woken up because of expired timer.
3139 */
3140static void
3141spoe_check_timeouts(struct stream *s, struct filter *filter)
3142{
3143 struct spoe_context *ctx = filter->ctx;
3144
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003145 if (tick_is_expired(ctx->process_exp, now_ms)) {
3146 s->pending_events |= TASK_WOKEN_MSG;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003147 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003148 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003149}
3150
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003151/* Called when we are ready to filter data on a channel */
3152static int
3153spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3154{
3155 struct spoe_context *ctx = filter->ctx;
3156 int ret = 1;
3157
3158 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3159 " - ctx-flags=0x%08x\n",
3160 (int)now.tv_sec, (int)now.tv_usec,
3161 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3162 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3163
Christopher Fauletb067b062017-01-04 16:39:11 +01003164 if (ctx->state == SPOE_CTX_ST_NONE)
3165 goto out;
3166
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003167 if (!(chn->flags & CF_ISRESP)) {
3168 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
3169 chn->analysers |= AN_REQ_INSPECT_FE;
3170 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
3171 chn->analysers |= AN_REQ_INSPECT_BE;
3172
3173 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
3174 goto out;
3175
3176 ctx->stream_id = s->uniq_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003177 ret = spoe_process_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
Christopher Fauletb067b062017-01-04 16:39:11 +01003178 if (!ret)
3179 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003180 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
3181 }
3182 else {
3183 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
3184 chn->analysers |= AN_RES_INSPECT;
3185
3186 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
3187 goto out;
3188
Christopher Faulet8ef75252017-02-20 22:56:03 +01003189 ret = spoe_process_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003190 if (!ret) {
3191 channel_dont_read(chn);
3192 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01003193 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003194 }
Christopher Fauletb067b062017-01-04 16:39:11 +01003195 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003196 }
3197
3198 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003199 return ret;
3200}
3201
3202/* Called before a processing happens on a given channel */
3203static int
3204spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
3205 struct channel *chn, unsigned an_bit)
3206{
3207 struct spoe_context *ctx = filter->ctx;
3208 int ret = 1;
3209
3210 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3211 " - ctx-flags=0x%08x - ana=0x%08x\n",
3212 (int)now.tv_sec, (int)now.tv_usec,
3213 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3214 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
3215 ctx->flags, an_bit);
3216
Christopher Fauletb067b062017-01-04 16:39:11 +01003217 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003218 goto out;
3219
3220 switch (an_bit) {
3221 case AN_REQ_INSPECT_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003222 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003223 break;
3224 case AN_REQ_INSPECT_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003225 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003226 break;
3227 case AN_RES_INSPECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003228 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003229 break;
3230 case AN_REQ_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003231 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003232 break;
3233 case AN_REQ_HTTP_PROCESS_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003234 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003235 break;
3236 case AN_RES_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003237 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003238 break;
3239 }
3240
3241 out:
3242 if (!ret) {
3243 channel_dont_read(chn);
3244 channel_dont_close(chn);
3245 }
3246 return ret;
3247}
3248
3249/* Called when the filtering on the channel ends. */
3250static int
3251spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3252{
3253 struct spoe_context *ctx = filter->ctx;
3254
3255 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3256 " - ctx-flags=0x%08x\n",
3257 (int)now.tv_sec, (int)now.tv_usec,
3258 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3259 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3260
3261 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003262 spoe_reset_context(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003263 }
3264
3265 return 1;
3266}
3267
3268/********************************************************************
3269 * Functions that manage the filter initialization
3270 ********************************************************************/
3271struct flt_ops spoe_ops = {
3272 /* Manage SPOE filter, called for each filter declaration */
3273 .init = spoe_init,
3274 .deinit = spoe_deinit,
3275 .check = spoe_check,
3276
3277 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01003278 .attach = spoe_start,
3279 .detach = spoe_stop,
3280 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003281
3282 /* Handle channels activity */
3283 .channel_start_analyze = spoe_start_analyze,
3284 .channel_pre_analyze = spoe_chn_pre_analyze,
3285 .channel_end_analyze = spoe_end_analyze,
3286};
3287
3288
3289static int
3290cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
3291{
3292 const char *err;
3293 int i, err_code = 0;
3294
3295 if ((cfg_scope == NULL && curengine != NULL) ||
3296 (cfg_scope != NULL && curengine == NULL) ||
3297 strcmp(curengine, cfg_scope))
3298 goto out;
3299
3300 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
3301 if (!*args[1]) {
3302 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
3303 file, linenum);
3304 err_code |= ERR_ALERT | ERR_ABORT;
3305 goto out;
3306 }
3307 if (*args[2]) {
3308 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3309 file, linenum, args[2]);
3310 err_code |= ERR_ALERT | ERR_ABORT;
3311 goto out;
3312 }
3313
3314 err = invalid_char(args[1]);
3315 if (err) {
3316 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3317 file, linenum, *err, args[0], args[1]);
3318 err_code |= ERR_ALERT | ERR_ABORT;
3319 goto out;
3320 }
3321
3322 if (curagent != NULL) {
3323 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
3324 file, linenum);
3325 err_code |= ERR_ALERT | ERR_ABORT;
3326 goto out;
3327 }
3328 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
3329 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3330 err_code |= ERR_ALERT | ERR_ABORT;
3331 goto out;
3332 }
3333
3334 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003335
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003336 curagent->conf.file = strdup(file);
3337 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003338
3339 curagent->timeout.hello = TICK_ETERNITY;
3340 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003341 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003342
3343 curagent->engine_id = NULL;
3344 curagent->var_pfx = NULL;
3345 curagent->var_on_error = NULL;
3346 curagent->flags = 0;
3347 curagent->cps_max = 0;
3348 curagent->eps_max = 0;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003349 curagent->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003350 curagent->min_applets = 0;
3351 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003352
3353 for (i = 0; i < SPOE_EV_EVENTS; ++i)
3354 LIST_INIT(&curagent->messages[i]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003355
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003356 curagent->frame_size = curagent->max_frame_size;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003357 curagent->applets_act = 0;
3358 curagent->applets_idle = 0;
3359 curagent->sending_rate = 0;
3360
3361 LIST_INIT(&curagent->applets);
3362 LIST_INIT(&curagent->sending_queue);
3363 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003364 }
3365 else if (!strcmp(args[0], "use-backend")) {
3366 if (!*args[1]) {
3367 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3368 file, linenum, args[0]);
3369 err_code |= ERR_ALERT | ERR_FATAL;
3370 goto out;
3371 }
3372 if (*args[2]) {
3373 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3374 file, linenum, args[2]);
3375 err_code |= ERR_ALERT | ERR_ABORT;
3376 goto out;
3377 }
3378 free(curagent->b.name);
3379 curagent->b.name = strdup(args[1]);
3380 }
3381 else if (!strcmp(args[0], "messages")) {
3382 int cur_arg = 1;
3383 while (*args[cur_arg]) {
3384 struct spoe_msg_placeholder *mp = NULL;
3385
3386 list_for_each_entry(mp, &curmps, list) {
3387 if (!strcmp(mp->id, args[cur_arg])) {
3388 Alert("parsing [%s:%d]: spoe-message message '%s' already declared.\n",
3389 file, linenum, args[cur_arg]);
3390 err_code |= ERR_ALERT | ERR_FATAL;
3391 goto out;
3392 }
3393 }
3394
3395 if ((mp = calloc(1, sizeof(*mp))) == NULL) {
3396 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3397 err_code |= ERR_ALERT | ERR_ABORT;
3398 goto out;
3399 }
3400 mp->id = strdup(args[cur_arg]);
3401 LIST_ADDQ(&curmps, &mp->list);
3402 cur_arg++;
3403 }
3404 }
3405 else if (!strcmp(args[0], "timeout")) {
3406 unsigned int *tv = NULL;
3407 const char *res;
3408 unsigned timeout;
3409
3410 if (!*args[1]) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003411 Alert("parsing [%s:%d] : 'timeout' expects 'hello', 'idle' and 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003412 file, linenum);
3413 err_code |= ERR_ALERT | ERR_FATAL;
3414 goto out;
3415 }
3416 if (!strcmp(args[1], "hello"))
3417 tv = &curagent->timeout.hello;
3418 else if (!strcmp(args[1], "idle"))
3419 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003420 else if (!strcmp(args[1], "processing"))
3421 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003422 else {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003423 Alert("parsing [%s:%d] : 'timeout' supports 'hello', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003424 file, linenum, args[1]);
3425 err_code |= ERR_ALERT | ERR_FATAL;
3426 goto out;
3427 }
3428 if (!*args[2]) {
3429 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3430 file, linenum, args[1]);
3431 err_code |= ERR_ALERT | ERR_FATAL;
3432 goto out;
3433 }
3434 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3435 if (res) {
3436 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3437 file, linenum, *res, args[1]);
3438 err_code |= ERR_ALERT | ERR_ABORT;
3439 goto out;
3440 }
3441 if (*args[3]) {
3442 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3443 file, linenum, args[3]);
3444 err_code |= ERR_ALERT | ERR_ABORT;
3445 goto out;
3446 }
3447 *tv = MS_TO_TICKS(timeout);
3448 }
3449 else if (!strcmp(args[0], "option")) {
3450 if (!*args[1]) {
3451 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3452 file, linenum, args[0]);
3453 err_code |= ERR_ALERT | ERR_FATAL;
3454 goto out;
3455 }
3456 if (!strcmp(args[1], "var-prefix")) {
3457 char *tmp;
3458
3459 if (!*args[2]) {
3460 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3461 file, linenum, args[0],
3462 args[1]);
3463 err_code |= ERR_ALERT | ERR_FATAL;
3464 goto out;
3465 }
3466 tmp = args[2];
3467 while (*tmp) {
3468 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3469 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3470 file, linenum, args[0], args[1]);
3471 err_code |= ERR_ALERT | ERR_FATAL;
3472 goto out;
3473 }
3474 tmp++;
3475 }
3476 curagent->var_pfx = strdup(args[2]);
3477 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003478 else if (!strcmp(args[1], "continue-on-error")) {
3479 if (*args[2]) {
3480 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
Christopher Faulet48026722016-11-16 15:01:12 +01003481 file, linenum, args[2]);
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003482 err_code |= ERR_ALERT | ERR_ABORT;
3483 goto out;
3484 }
3485 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3486 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003487 else if (!strcmp(args[1], "set-on-error")) {
3488 char *tmp;
3489
3490 if (!*args[2]) {
3491 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3492 file, linenum, args[0],
3493 args[1]);
3494 err_code |= ERR_ALERT | ERR_FATAL;
3495 goto out;
3496 }
3497 tmp = args[2];
3498 while (*tmp) {
3499 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3500 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3501 file, linenum, args[0], args[1]);
3502 err_code |= ERR_ALERT | ERR_FATAL;
3503 goto out;
3504 }
3505 tmp++;
3506 }
3507 curagent->var_on_error = strdup(args[2]);
3508 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003509 else {
3510 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3511 file, linenum, args[1]);
3512 err_code |= ERR_ALERT | ERR_FATAL;
3513 goto out;
3514 }
Christopher Faulet48026722016-11-16 15:01:12 +01003515 }
3516 else if (!strcmp(args[0], "maxconnrate")) {
3517 if (!*args[1]) {
3518 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3519 file, linenum, args[0]);
3520 err_code |= ERR_ALERT | ERR_FATAL;
3521 goto out;
3522 }
3523 if (*args[2]) {
3524 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3525 file, linenum, args[2]);
3526 err_code |= ERR_ALERT | ERR_ABORT;
3527 goto out;
3528 }
3529 curagent->cps_max = atol(args[1]);
3530 }
3531 else if (!strcmp(args[0], "maxerrrate")) {
3532 if (!*args[1]) {
3533 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3534 file, linenum, args[0]);
3535 err_code |= ERR_ALERT | ERR_FATAL;
3536 goto out;
3537 }
3538 if (*args[2]) {
3539 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3540 file, linenum, args[2]);
3541 err_code |= ERR_ALERT | ERR_ABORT;
3542 goto out;
3543 }
3544 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003545 }
3546 else if (*args[0]) {
3547 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3548 file, linenum, args[0]);
3549 err_code |= ERR_ALERT | ERR_FATAL;
3550 goto out;
3551 }
3552 out:
3553 return err_code;
3554}
3555
3556static int
3557cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3558{
3559 struct spoe_message *msg;
3560 struct spoe_arg *arg;
3561 const char *err;
3562 char *errmsg = NULL;
3563 int err_code = 0;
3564
3565 if ((cfg_scope == NULL && curengine != NULL) ||
3566 (cfg_scope != NULL && curengine == NULL) ||
3567 strcmp(curengine, cfg_scope))
3568 goto out;
3569
3570 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3571 if (!*args[1]) {
3572 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3573 file, linenum);
3574 err_code |= ERR_ALERT | ERR_ABORT;
3575 goto out;
3576 }
3577 if (*args[2]) {
3578 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3579 file, linenum, args[2]);
3580 err_code |= ERR_ALERT | ERR_ABORT;
3581 goto out;
3582 }
3583
3584 err = invalid_char(args[1]);
3585 if (err) {
3586 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3587 file, linenum, *err, args[0], args[1]);
3588 err_code |= ERR_ALERT | ERR_ABORT;
3589 goto out;
3590 }
3591
3592 list_for_each_entry(msg, &curmsgs, list) {
3593 if (!strcmp(msg->id, args[1])) {
3594 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3595 " name as another one declared at %s:%d.\n",
3596 file, linenum, args[1], msg->conf.file, msg->conf.line);
3597 err_code |= ERR_ALERT | ERR_FATAL;
3598 goto out;
3599 }
3600 }
3601
3602 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3603 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3604 err_code |= ERR_ALERT | ERR_ABORT;
3605 goto out;
3606 }
3607
3608 curmsg->id = strdup(args[1]);
3609 curmsg->id_len = strlen(curmsg->id);
3610 curmsg->event = SPOE_EV_NONE;
3611 curmsg->conf.file = strdup(file);
3612 curmsg->conf.line = linenum;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003613 curmsg->nargs = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003614 LIST_INIT(&curmsg->args);
3615 LIST_ADDQ(&curmsgs, &curmsg->list);
3616 }
3617 else if (!strcmp(args[0], "args")) {
3618 int cur_arg = 1;
3619
3620 curproxy->conf.args.ctx = ARGC_SPOE;
3621 curproxy->conf.args.file = file;
3622 curproxy->conf.args.line = linenum;
3623 while (*args[cur_arg]) {
3624 char *delim = strchr(args[cur_arg], '=');
3625 int idx = 0;
3626
3627 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3628 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3629 err_code |= ERR_ALERT | ERR_ABORT;
3630 goto out;
3631 }
3632
3633 if (!delim) {
3634 arg->name = NULL;
3635 arg->name_len = 0;
3636 delim = args[cur_arg];
3637 }
3638 else {
3639 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3640 arg->name_len = delim - args[cur_arg];
3641 delim++;
3642 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003643 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3644 &idx, file, linenum, &errmsg,
3645 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003646 if (arg->expr == NULL) {
3647 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3648 err_code |= ERR_ALERT | ERR_FATAL;
3649 free(arg->name);
3650 free(arg);
3651 goto out;
3652 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003653 curmsg->nargs++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003654 LIST_ADDQ(&curmsg->args, &arg->list);
3655 cur_arg++;
3656 }
3657 curproxy->conf.args.file = NULL;
3658 curproxy->conf.args.line = 0;
3659 }
3660 else if (!strcmp(args[0], "event")) {
3661 if (!*args[1]) {
3662 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
3663 err_code |= ERR_ALERT | ERR_ABORT;
3664 goto out;
3665 }
3666 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3667 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3668 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3669 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3670
3671 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3672 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3673 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3674 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3675 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3676 curmsg->event = SPOE_EV_ON_TCP_RSP;
3677
3678 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3679 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3680 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3681 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3682 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3683 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3684 else {
3685 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3686 file, linenum, args[1]);
3687 err_code |= ERR_ALERT | ERR_ABORT;
3688 goto out;
3689 }
3690 }
3691 else if (!*args[0]) {
3692 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3693 file, linenum, args[0]);
3694 err_code |= ERR_ALERT | ERR_FATAL;
3695 goto out;
3696 }
3697 out:
3698 free(errmsg);
3699 return err_code;
3700}
3701
3702/* Return -1 on error, else 0 */
3703static int
3704parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
3705 struct flt_conf *fconf, char **err, void *private)
3706{
3707 struct list backup_sections;
3708 struct spoe_config *conf;
3709 struct spoe_message *msg, *msgback;
3710 struct spoe_msg_placeholder *mp, *mpback;
3711 char *file = NULL, *engine = NULL;
3712 int ret, pos = *cur_arg + 1;
3713
3714 conf = calloc(1, sizeof(*conf));
3715 if (conf == NULL) {
3716 memprintf(err, "%s: out of memory", args[*cur_arg]);
3717 goto error;
3718 }
3719 conf->proxy = px;
3720
3721 while (*args[pos]) {
3722 if (!strcmp(args[pos], "config")) {
3723 if (!*args[pos+1]) {
3724 memprintf(err, "'%s' : '%s' option without value",
3725 args[*cur_arg], args[pos]);
3726 goto error;
3727 }
3728 file = args[pos+1];
3729 pos += 2;
3730 }
3731 else if (!strcmp(args[pos], "engine")) {
3732 if (!*args[pos+1]) {
3733 memprintf(err, "'%s' : '%s' option without value",
3734 args[*cur_arg], args[pos]);
3735 goto error;
3736 }
3737 engine = args[pos+1];
3738 pos += 2;
3739 }
3740 else {
3741 memprintf(err, "unknown keyword '%s'", args[pos]);
3742 goto error;
3743 }
3744 }
3745 if (file == NULL) {
3746 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
3747 goto error;
3748 }
3749
3750 /* backup sections and register SPOE sections */
3751 LIST_INIT(&backup_sections);
3752 cfg_backup_sections(&backup_sections);
3753 cfg_register_section("spoe-agent", cfg_parse_spoe_agent);
3754 cfg_register_section("spoe-message", cfg_parse_spoe_message);
3755
3756 /* Parse SPOE filter configuration file */
3757 curengine = engine;
3758 curproxy = px;
3759 curagent = NULL;
3760 curmsg = NULL;
3761 ret = readcfgfile(file);
3762 curproxy = NULL;
3763
3764 /* unregister SPOE sections and restore previous sections */
3765 cfg_unregister_sections();
3766 cfg_restore_sections(&backup_sections);
3767
3768 if (ret == -1) {
3769 memprintf(err, "Could not open configuration file %s : %s",
3770 file, strerror(errno));
3771 goto error;
3772 }
3773 if (ret & (ERR_ABORT|ERR_FATAL)) {
3774 memprintf(err, "Error(s) found in configuration file %s", file);
3775 goto error;
3776 }
3777
3778 /* Check SPOE agent */
3779 if (curagent == NULL) {
3780 memprintf(err, "No SPOE agent found in file %s", file);
3781 goto error;
3782 }
3783 if (curagent->b.name == NULL) {
3784 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
3785 curagent->id, curagent->conf.file, curagent->conf.line);
3786 goto error;
3787 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003788 if (curagent->timeout.hello == TICK_ETERNITY ||
3789 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01003790 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003791 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
3792 " | While not properly invalid, you will certainly encounter various problems\n"
3793 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01003794 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003795 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3796 }
3797 if (curagent->var_pfx == NULL) {
3798 char *tmp = curagent->id;
3799
3800 while (*tmp) {
3801 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3802 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
3803 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
3804 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
3805 goto error;
3806 }
3807 tmp++;
3808 }
3809 curagent->var_pfx = strdup(curagent->id);
3810 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01003811 if (curagent->engine_id == NULL)
3812 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003813
3814 if (LIST_ISEMPTY(&curmps)) {
3815 Warning("Proxy '%s': No message used by SPOE agent '%s' declared at %s:%d.\n",
3816 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3817 goto finish;
3818 }
3819
3820 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3821 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01003822 struct spoe_arg *arg;
3823 unsigned int where;
3824
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003825 if (!strcmp(msg->id, mp->id)) {
3826 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
3827 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
3828 msg->event = SPOE_EV_ON_TCP_REQ_FE;
3829 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
3830 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
3831 }
3832 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
3833 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
3834 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
3835 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
3836 px->id, msg->conf.file, msg->conf.line);
3837 goto next;
3838 }
3839 if (msg->event == SPOE_EV_NONE) {
3840 Warning("Proxy '%s': Ignore SPOE message without event at %s:%d.\n",
3841 px->id, msg->conf.file, msg->conf.line);
3842 goto next;
3843 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01003844
3845 where = 0;
3846 switch (msg->event) {
3847 case SPOE_EV_ON_CLIENT_SESS:
3848 where |= SMP_VAL_FE_CON_ACC;
3849 break;
3850
3851 case SPOE_EV_ON_TCP_REQ_FE:
3852 where |= SMP_VAL_FE_REQ_CNT;
3853 break;
3854
3855 case SPOE_EV_ON_HTTP_REQ_FE:
3856 where |= SMP_VAL_FE_HRQ_HDR;
3857 break;
3858
3859 case SPOE_EV_ON_TCP_REQ_BE:
3860 if (px->cap & PR_CAP_FE)
3861 where |= SMP_VAL_FE_REQ_CNT;
3862 if (px->cap & PR_CAP_BE)
3863 where |= SMP_VAL_BE_REQ_CNT;
3864 break;
3865
3866 case SPOE_EV_ON_HTTP_REQ_BE:
3867 if (px->cap & PR_CAP_FE)
3868 where |= SMP_VAL_FE_HRQ_HDR;
3869 if (px->cap & PR_CAP_BE)
3870 where |= SMP_VAL_BE_HRQ_HDR;
3871 break;
3872
3873 case SPOE_EV_ON_SERVER_SESS:
3874 where |= SMP_VAL_BE_SRV_CON;
3875 break;
3876
3877 case SPOE_EV_ON_TCP_RSP:
3878 if (px->cap & PR_CAP_FE)
3879 where |= SMP_VAL_FE_RES_CNT;
3880 if (px->cap & PR_CAP_BE)
3881 where |= SMP_VAL_BE_RES_CNT;
3882 break;
3883
3884 case SPOE_EV_ON_HTTP_RSP:
3885 if (px->cap & PR_CAP_FE)
3886 where |= SMP_VAL_FE_HRS_HDR;
3887 if (px->cap & PR_CAP_BE)
3888 where |= SMP_VAL_BE_HRS_HDR;
3889 break;
3890
3891 default:
3892 break;
3893 }
3894
3895 list_for_each_entry(arg, &msg->args, list) {
3896 if (!(arg->expr->fetch->val & where)) {
3897 Warning("Proxy '%s': Ignore SPOE message at %s:%d: "
3898 "some args extract information from '%s', "
3899 "none of which is available here ('%s').\n",
3900 px->id, msg->conf.file, msg->conf.line,
3901 sample_ckp_names(arg->expr->fetch->use),
3902 sample_ckp_names(where));
3903 goto next;
3904 }
3905 }
3906
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003907 msg->agent = curagent;
3908 LIST_DEL(&msg->list);
3909 LIST_ADDQ(&curagent->messages[msg->event], &msg->list);
3910 goto next;
3911 }
3912 }
3913 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
3914 curagent->id, mp->id, curagent->conf.file, curagent->conf.line);
3915 goto error;
3916 next:
3917 continue;
3918 }
3919
3920 finish:
3921 conf->agent = curagent;
3922 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3923 LIST_DEL(&mp->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003924 spoe_release_msg_placeholder(mp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003925 }
3926 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3927 Warning("Proxy '%s': Ignore unused SPOE messages '%s' declared at %s:%d.\n",
3928 px->id, msg->id, msg->conf.file, msg->conf.line);
3929 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003930 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003931 }
3932
3933 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01003934 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003935 fconf->ops = &spoe_ops;
3936 fconf->conf = conf;
3937 return 0;
3938
3939 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003940 spoe_release_agent(curagent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003941 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3942 LIST_DEL(&mp->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003943 spoe_release_msg_placeholder(mp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003944 }
3945 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3946 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003947 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003948 }
3949 free(conf);
3950 return -1;
3951}
3952
3953
3954/* Declare the filter parser for "spoe" keyword */
3955static struct flt_kw_list flt_kws = { "SPOE", { }, {
3956 { "spoe", parse_spoe_flt, NULL },
3957 { NULL, NULL, NULL },
3958 }
3959};
3960
3961__attribute__((constructor))
3962static void __spoe_init(void)
3963{
3964 flt_register_keywords(&flt_kws);
3965
3966 LIST_INIT(&curmsgs);
3967 LIST_INIT(&curmps);
3968 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003969 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003970}
3971
3972__attribute__((destructor))
3973static void
3974__spoe_deinit(void)
3975{
3976 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003977 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003978}