blob: 7fc4ed87f3370eeea757e53f237ce0c04d1dc1b4 [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>
Emeric Bruna1dd2432017-06-21 15:42:52 +020021#include <common/hathreads.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020022
23#include <types/arg.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020024#include <types/global.h>
Christopher Faulet1f40b912017-02-17 09:32:19 +010025#include <types/spoe.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020026
Christopher Faulet57583e42017-09-04 15:41:09 +020027#include <proto/acl.h>
Christopher Faulet76c09ef2017-09-21 11:03:52 +020028#include <proto/action.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020029#include <proto/arg.h>
30#include <proto/backend.h>
31#include <proto/filters.h>
Christopher Faulet48026722016-11-16 15:01:12 +010032#include <proto/freq_ctr.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020033#include <proto/frontend.h>
34#include <proto/log.h>
35#include <proto/proto_http.h>
36#include <proto/proxy.h>
37#include <proto/sample.h>
38#include <proto/session.h>
39#include <proto/signal.h>
Christopher Faulet4ff3e572017-02-24 14:31:11 +010040#include <proto/spoe.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020041#include <proto/stream.h>
42#include <proto/stream_interface.h>
43#include <proto/task.h>
Christopher Faulet76c09ef2017-09-21 11:03:52 +020044#include <proto/tcp_rules.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020045#include <proto/vars.h>
46
47#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
48#define SPOE_PRINTF(x...) fprintf(x)
49#else
50#define SPOE_PRINTF(x...)
51#endif
52
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010053/* Reserved 4 bytes to the frame size. So a frame and its size can be written
54 * together in a buffer */
55#define MAX_FRAME_SIZE global.tune.bufsize - 4
56
57/* The minimum size for a frame */
58#define MIN_FRAME_SIZE 256
59
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010060/* Reserved for the metadata and the frame type.
61 * So <MAX_FRAME_SIZE> - <FRAME_HDR_SIZE> is the maximum payload size */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010062#define FRAME_HDR_SIZE 32
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020063
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010064/* Helper to get SPOE ctx inside an appctx */
Christopher Faulet42bfa462017-01-04 14:14:19 +010065#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
66
Christopher Faulet3b386a32017-02-23 10:17:15 +010067/* SPOE filter id. Used to identify SPOE filters */
68const char *spoe_filter_id = "SPOE filter";
69
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020070/* Set if the handle on SIGUSR1 is registered */
71static int sighandler_registered = 0;
72
73/* proxy used during the parsing */
74struct proxy *curproxy = NULL;
75
76/* The name of the SPOE engine, used during the parsing */
77char *curengine = NULL;
78
79/* SPOE agent used during the parsing */
Christopher Faulet11610f32017-09-21 10:23:10 +020080/* SPOE agent/group/message used during the parsing */
81struct spoe_agent *curagent = NULL;
82struct spoe_group *curgrp = NULL;
83struct spoe_message *curmsg = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020084
85/* list of SPOE messages and placeholders used during the parsing */
86struct list curmsgs;
Christopher Faulet11610f32017-09-21 10:23:10 +020087struct list curgrps;
88struct list curmphs;
89struct list curgphs;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020090
Christopher Faulet42bfa462017-01-04 14:14:19 +010091/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020092static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +010093static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020094
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020095struct flt_ops spoe_ops;
96
Christopher Faulet8ef75252017-02-20 22:56:03 +010097static int spoe_queue_context(struct spoe_context *ctx);
98static int spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
99static void spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200100
101/********************************************************************
102 * helper functions/globals
103 ********************************************************************/
104static void
Christopher Faulet11610f32017-09-21 10:23:10 +0200105spoe_release_placeholder(struct spoe_placeholder *ph)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200106{
Christopher Faulet11610f32017-09-21 10:23:10 +0200107 if (!ph)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200108 return;
Christopher Faulet11610f32017-09-21 10:23:10 +0200109 free(ph->id);
110 free(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200111}
112
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200113static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100114spoe_release_message(struct spoe_message *msg)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200115{
Christopher Faulet57583e42017-09-04 15:41:09 +0200116 struct spoe_arg *arg, *argback;
117 struct acl *acl, *aclback;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200118
119 if (!msg)
120 return;
121 free(msg->id);
122 free(msg->conf.file);
Christopher Faulet57583e42017-09-04 15:41:09 +0200123 list_for_each_entry_safe(arg, argback, &msg->args, list) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200124 release_sample_expr(arg->expr);
125 free(arg->name);
126 LIST_DEL(&arg->list);
127 free(arg);
128 }
Christopher Faulet57583e42017-09-04 15:41:09 +0200129 list_for_each_entry_safe(acl, aclback, &msg->acls, list) {
130 LIST_DEL(&acl->list);
131 prune_acl(acl);
132 free(acl);
133 }
134 if (msg->cond) {
135 prune_acl_cond(msg->cond);
136 free(msg->cond);
137 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200138 free(msg);
139}
140
141static void
Christopher Faulet11610f32017-09-21 10:23:10 +0200142spoe_release_group(struct spoe_group *grp)
143{
144 if (!grp)
145 return;
146 free(grp->id);
147 free(grp->conf.file);
148 free(grp);
149}
150
151static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100152spoe_release_agent(struct spoe_agent *agent)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200153{
Christopher Faulet11610f32017-09-21 10:23:10 +0200154 struct spoe_message *msg, *msgback;
155 struct spoe_group *grp, *grpback;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200156
157 if (!agent)
158 return;
159 free(agent->id);
160 free(agent->conf.file);
161 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100162 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100163 free(agent->var_on_error);
Christopher Faulet11610f32017-09-21 10:23:10 +0200164 list_for_each_entry_safe(msg, msgback, &agent->messages, list) {
165 LIST_DEL(&msg->list);
166 spoe_release_message(msg);
167 }
168 list_for_each_entry_safe(grp, grpback, &agent->groups, list) {
169 LIST_DEL(&grp->list);
170 spoe_release_group(grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200171 }
172 free(agent);
173}
174
175static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100176 [SPOE_FRM_ERR_NONE] = "normal",
177 [SPOE_FRM_ERR_IO] = "I/O error",
178 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
179 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
180 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
181 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
182 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
183 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
184 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
185 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
186 [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
187 [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100188 [SPOE_FRM_ERR_FRAMEID_NOTFOUND] = "frame-id not found",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100189 [SPOE_FRM_ERR_RES] = "resource allocation error",
190 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200191};
192
193static const char *spoe_event_str[SPOE_EV_EVENTS] = {
194 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
195 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
196 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
197 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
198 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
199
200 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
201 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
202 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
203};
204
205
206#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
207
208static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100209 [SPOE_CTX_ST_NONE] = "NONE",
210 [SPOE_CTX_ST_READY] = "READY",
211 [SPOE_CTX_ST_ENCODING_MSGS] = "ENCODING_MSGS",
212 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
213 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
214 [SPOE_CTX_ST_DONE] = "DONE",
215 [SPOE_CTX_ST_ERROR] = "ERROR",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200216};
217
218static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100219 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
220 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
221 [SPOE_APPCTX_ST_IDLE] = "IDLE",
222 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
223 [SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY] = "SENDING_FRAG_NOTIFY",
224 [SPOE_APPCTX_ST_WAITING_SYNC_ACK] = "WAITING_SYNC_ACK",
225 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
226 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
227 [SPOE_APPCTX_ST_EXIT] = "EXIT",
228 [SPOE_APPCTX_ST_END] = "END",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200229};
230
231#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100232
Christopher Faulet8ef75252017-02-20 22:56:03 +0100233/* Used to generates a unique id for an engine. On success, it returns a
234 * allocated string. So it is the caller's reponsibility to release it. If the
235 * allocation failed, it returns NULL. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100236static char *
237generate_pseudo_uuid()
238{
239 static int init = 0;
240
241 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
242 const char uuid_chr[] = "0123456789ABCDEF-";
243 char *uuid;
244 int i;
245
246 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
247 return NULL;
248
249 if (!init) {
250 srand(now_ms);
251 init = 1;
252 }
253
254 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
255 int r = rand () % 16;
256
257 switch (uuid_fmt[i]) {
258 case 'x' : uuid[i] = uuid_chr[r]; break;
259 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
260 default : uuid[i] = uuid_fmt[i]; break;
261 }
262 }
263 return uuid;
264}
265
Christopher Faulet8ef75252017-02-20 22:56:03 +0100266/* Returns the minimum number of appets alive at a time. This function is used
267 * to know if more applets should be created for an engine. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100268static inline unsigned int
269min_applets_act(struct spoe_agent *agent)
270{
271 unsigned int nbsrv;
272
Christopher Faulet8ef75252017-02-20 22:56:03 +0100273 /* TODO: Add a config parameter to customize this value. Always 0 for
274 * now */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100275 if (agent->min_applets)
276 return agent->min_applets;
277
Christopher Faulet8ef75252017-02-20 22:56:03 +0100278 /* Get the number of active servers for the backend */
279 nbsrv = (agent->b.be->srv_act
280 ? agent->b.be->srv_act
281 : agent->b.be->srv_bck);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100282 return 2*nbsrv;
283}
284
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200285/********************************************************************
286 * Functions that encode/decode SPOE frames
287 ********************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200288/* Helper to get static string length, excluding the terminating null byte */
289#define SLEN(str) (sizeof(str)-1)
290
291/* Predefined key used in HELLO/DISCONNECT frames */
292#define SUPPORTED_VERSIONS_KEY "supported-versions"
293#define VERSION_KEY "version"
294#define MAX_FRAME_SIZE_KEY "max-frame-size"
295#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100296#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100297#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200298#define STATUS_CODE_KEY "status-code"
299#define MSG_KEY "message"
300
301struct spoe_version {
302 char *str;
303 int min;
304 int max;
305};
306
307/* All supported versions */
308static struct spoe_version supported_versions[] = {
309 {"1.0", 1000, 1000},
310 {NULL, 0, 0}
311};
312
313/* Comma-separated list of supported versions */
314#define SUPPORTED_VERSIONS_VAL "1.0"
315
Christopher Faulet8ef75252017-02-20 22:56:03 +0100316/* Convert a string to a SPOE version value. The string must follow the format
317 * "MAJOR.MINOR". It will be concerted into the integer (1000 * MAJOR + MINOR).
318 * If an error occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200319static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100320spoe_str_to_vsn(const char *str, size_t len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200321{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100322 const char *p, *end;
323 int maj, min, vsn;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200324
Christopher Faulet8ef75252017-02-20 22:56:03 +0100325 p = str;
326 end = str+len;
327 maj = min = 0;
328 vsn = -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200329
Christopher Faulet8ef75252017-02-20 22:56:03 +0100330 /* skip leading spaces */
331 while (p < end && isspace(*p))
332 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200333
Christopher Faulet8ef75252017-02-20 22:56:03 +0100334 /* parse Major number, until the '.' */
335 while (*p != '.') {
336 if (p >= end || *p < '0' || *p > '9')
337 goto out;
338 maj *= 10;
339 maj += (*p - '0');
340 p++;
341 }
342
343 /* check Major version */
344 if (!maj)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200345 goto out;
346
Christopher Faulet8ef75252017-02-20 22:56:03 +0100347 p++; /* skip the '.' */
348 if (p >= end || *p < '0' || *p > '9') /* Minor number is missing */
349 goto out;
350
351 /* Parse Minor number */
352 while (p < end) {
353 if (*p < '0' || *p > '9')
354 break;
355 min *= 10;
356 min += (*p - '0');
357 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200358 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100359
360 /* check Minor number */
361 if (min > 999)
362 goto out;
363
364 /* skip trailing spaces */
365 while (p < end && isspace(*p))
366 p++;
367 if (p != end)
368 goto out;
369
370 vsn = maj * 1000 + min;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200371 out:
372 return vsn;
373}
374
Christopher Faulet8ef75252017-02-20 22:56:03 +0100375/* Encode the HELLO frame sent by HAProxy to an agent. It returns the number of
376 * encoded bytes in the frame on success, 0 if an encoding error occured and -1
377 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200378static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100379spoe_prepare_hahello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200380{
Christopher Faulet305c6072017-02-23 16:17:53 +0100381 struct chunk *chk;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100382 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100383 char *p, *end;
384 unsigned int flags = SPOE_FRM_FL_FIN;
385 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200386
Christopher Faulet8ef75252017-02-20 22:56:03 +0100387 p = frame;
388 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200389
Christopher Faulet8ef75252017-02-20 22:56:03 +0100390 /* Set Frame type */
391 *p++ = SPOE_FRM_T_HAPROXY_HELLO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200392
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100393 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100394 memcpy(p, (char *)&flags, 4);
395 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200396
397 /* No stream-id and frame-id for HELLO frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100398 *p++ = 0; *p++ = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200399
400 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
401 * and "capabilities" */
402
403 /* "supported-versions" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100404 sz = SLEN(SUPPORTED_VERSIONS_KEY);
405 if (spoe_encode_buffer(SUPPORTED_VERSIONS_KEY, sz, &p, end) == -1)
406 goto too_big;
407
408 *p++ = SPOE_DATA_T_STR;
409 sz = SLEN(SUPPORTED_VERSIONS_VAL);
410 if (spoe_encode_buffer(SUPPORTED_VERSIONS_VAL, sz, &p, end) == -1)
411 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200412
413 /* "max-fram-size" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100414 sz = SLEN(MAX_FRAME_SIZE_KEY);
415 if (spoe_encode_buffer(MAX_FRAME_SIZE_KEY, sz, &p, end) == -1)
416 goto too_big;
417
418 *p++ = SPOE_DATA_T_UINT32;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200419 if (encode_varint(SPOE_APPCTX(appctx)->max_frame_size, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100420 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200421
422 /* "capabilities" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100423 sz = SLEN(CAPABILITIES_KEY);
424 if (spoe_encode_buffer(CAPABILITIES_KEY, sz, &p, end) == -1)
425 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200426
Christopher Faulet8ef75252017-02-20 22:56:03 +0100427 *p++ = SPOE_DATA_T_STR;
Christopher Faulet305c6072017-02-23 16:17:53 +0100428 chk = get_trash_chunk();
429 if (agent != NULL && (agent->flags & SPOE_FL_PIPELINING)) {
430 memcpy(chk->str, "pipelining", 10);
431 chk->len += 10;
432 }
433 if (agent != NULL && (agent->flags & SPOE_FL_ASYNC)) {
434 if (chk->len) chk->str[chk->len++] = ',';
435 memcpy(chk->str+chk->len, "async", 5);
436 chk->len += 5;
437 }
Christopher Fauletcecd8522017-02-24 22:11:21 +0100438 if (agent != NULL && (agent->flags & SPOE_FL_RCV_FRAGMENTATION)) {
439 if (chk->len) chk->str[chk->len++] = ',';
440 memcpy(chk->str+chk->len, "fragmentation", 13);
441 chk->len += 5;
442 }
Christopher Faulet305c6072017-02-23 16:17:53 +0100443 if (spoe_encode_buffer(chk->str, chk->len, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100444 goto too_big;
445
446 /* (optionnal) "engine-id" K/V item, if present */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100447 if (agent != NULL && agent->engine_id != NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100448 sz = SLEN(ENGINE_ID_KEY);
449 if (spoe_encode_buffer(ENGINE_ID_KEY, sz, &p, end) == -1)
450 goto too_big;
451
452 *p++ = SPOE_DATA_T_STR;
453 sz = strlen(agent->engine_id);
454 if (spoe_encode_buffer(agent->engine_id, sz, &p, end) == -1)
455 goto too_big;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100456 }
457
Christopher Faulet8ef75252017-02-20 22:56:03 +0100458 return (p - frame);
459
460 too_big:
461 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
462 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200463}
464
Christopher Faulet8ef75252017-02-20 22:56:03 +0100465/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the number of
466 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
467 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200468static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100469spoe_prepare_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200470{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100471 const char *reason;
472 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100473 unsigned int flags = SPOE_FRM_FL_FIN;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100474 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200475
Christopher Faulet8ef75252017-02-20 22:56:03 +0100476 p = frame;
477 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200478
Christopher Faulet8ef75252017-02-20 22:56:03 +0100479 /* Set Frame type */
480 *p++ = SPOE_FRM_T_HAPROXY_DISCON;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200481
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100482 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100483 memcpy(p, (char *)&flags, 4);
484 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200485
486 /* No stream-id and frame-id for DISCONNECT frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100487 *p++ = 0; *p++ = 0;
488
489 if (SPOE_APPCTX(appctx)->status_code >= SPOE_FRM_ERRS)
490 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200491
492 /* There are 2 mandatory items: "status-code" and "message" */
493
494 /* "status-code" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100495 sz = SLEN(STATUS_CODE_KEY);
496 if (spoe_encode_buffer(STATUS_CODE_KEY, sz, &p, end) == -1)
497 goto too_big;
498
499 *p++ = SPOE_DATA_T_UINT32;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200500 if (encode_varint(SPOE_APPCTX(appctx)->status_code, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100501 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200502
503 /* "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100504 sz = SLEN(MSG_KEY);
505 if (spoe_encode_buffer(MSG_KEY, sz, &p, end) == -1)
506 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200507
Christopher Faulet8ef75252017-02-20 22:56:03 +0100508 /*Get the message corresponding to the status code */
509 reason = spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code];
510
511 *p++ = SPOE_DATA_T_STR;
512 sz = strlen(reason);
513 if (spoe_encode_buffer(reason, sz, &p, end) == -1)
514 goto too_big;
515
516 return (p - frame);
517
518 too_big:
519 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
520 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200521}
522
Christopher Faulet8ef75252017-02-20 22:56:03 +0100523/* Encode the NOTIFY frame sent by HAProxy to an agent. It returns the number of
524 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
525 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200526static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100527spoe_prepare_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
Christopher Fauleta1cda022016-12-21 08:58:06 +0100528 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200529{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100530 char *p, *end;
531 unsigned int stream_id, frame_id;
532 unsigned int flags = SPOE_FRM_FL_FIN;
533 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200534
Christopher Faulet8ef75252017-02-20 22:56:03 +0100535 p = frame;
536 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200537
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100538 stream_id = ctx->stream_id;
539 frame_id = ctx->frame_id;
540
541 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
542 /* The fragmentation is not supported by the applet */
543 if (!(SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_FRAGMENTATION)) {
544 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
545 return -1;
546 }
547 flags = ctx->frag_ctx.flags;
548 }
549
550 /* Set Frame type */
551 *p++ = SPOE_FRM_T_HAPROXY_NOTIFY;
552
553 /* Set flags */
554 memcpy(p, (char *)&flags, 4);
555 p += 4;
556
557 /* Set stream-id and frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200558 if (encode_varint(stream_id, &p, end) == -1)
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100559 goto too_big;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200560 if (encode_varint(frame_id, &p, end) == -1)
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100561 goto too_big;
562
563 /* Copy encoded messages, if possible */
564 sz = ctx->buffer->i;
565 if (p + sz >= end)
566 goto too_big;
567 memcpy(p, ctx->buffer->p, sz);
568 p += sz;
569
570 return (p - frame);
571
572 too_big:
573 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
574 return 0;
575}
576
577/* Encode next part of a fragmented frame sent by HAProxy to an agent. It
578 * returns the number of encoded bytes in the frame on success, 0 if an encoding
579 * error occurred and -1 if a fatal error occurred. */
580static int
581spoe_prepare_hafrag_frame(struct appctx *appctx, struct spoe_context *ctx,
582 char *frame, size_t size)
583{
584 char *p, *end;
585 unsigned int stream_id, frame_id;
586 unsigned int flags;
587 size_t sz;
588
589 p = frame;
590 end = frame+size;
591
Christopher Faulet8ef75252017-02-20 22:56:03 +0100592 /* <ctx> is null when the stream has aborted the processing of a
593 * fragmented frame. In this case, we must notify the corresponding
594 * agent using ids stored in <frag_ctx>. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100595 if (ctx == NULL) {
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100596 flags = (SPOE_FRM_FL_FIN|SPOE_FRM_FL_ABRT);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100597 stream_id = SPOE_APPCTX(appctx)->frag_ctx.cursid;
598 frame_id = SPOE_APPCTX(appctx)->frag_ctx.curfid;
599 }
600 else {
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100601 flags = ctx->frag_ctx.flags;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100602 stream_id = ctx->stream_id;
603 frame_id = ctx->frame_id;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100604 }
605
Christopher Faulet8ef75252017-02-20 22:56:03 +0100606 /* Set Frame type */
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100607 *p++ = SPOE_FRM_T_UNSET;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100608
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100609 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100610 memcpy(p, (char *)&flags, 4);
611 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200612
613 /* Set stream-id and frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200614 if (encode_varint(stream_id, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100615 goto too_big;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200616 if (encode_varint(frame_id, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100617 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200618
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100619 if (ctx == NULL)
620 goto end;
621
Christopher Faulet8ef75252017-02-20 22:56:03 +0100622 /* Copy encoded messages, if possible */
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100623 sz = ctx->buffer->i;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100624 if (p + sz >= end)
625 goto too_big;
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100626 memcpy(p, ctx->buffer->p, sz);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100627 p += sz;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100628
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100629 end:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100630 return (p - frame);
631
632 too_big:
633 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
634 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200635}
636
Christopher Faulet8ef75252017-02-20 22:56:03 +0100637/* Decode and process the HELLO frame sent by an agent. It returns the number of
638 * read bytes on success, 0 if a decoding error occurred, and -1 if a fatal
639 * error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200640static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100641spoe_handle_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200642{
Christopher Faulet305c6072017-02-23 16:17:53 +0100643 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
644 char *p, *end;
645 int vsn, max_frame_size;
646 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100647
648 p = frame;
649 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200650
651 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100652 if (*p++ != SPOE_FRM_T_AGENT_HELLO) {
653 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200654 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100655 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200656
Christopher Faulet8ef75252017-02-20 22:56:03 +0100657 if (size < 7 /* TYPE + METADATA */) {
658 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
659 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200660 }
661
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100662 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100663 memcpy((char *)&flags, p, 4);
664 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200665
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100666 /* Fragmentation is not supported for HELLO frame */
667 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100668 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100669 return -1;
670 }
671
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200672 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100673 if (*p != 0 || *(p+1) != 0) {
674 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
675 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200676 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100677 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200678
679 /* There are 3 mandatory items: "version", "max-frame-size" and
680 * "capabilities" */
681
682 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100683 vsn = max_frame_size = flags = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100684 while (p < end) {
685 char *str;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200686 uint64_t sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100687 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200688
689 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100690 ret = spoe_decode_buffer(&p, end, &str, &sz);
691 if (ret == -1 || !sz) {
692 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
693 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200694 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100695
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200696 /* Check "version" K/V item */
697 if (!memcmp(str, VERSION_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100698 int i, type = *p++;
699
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200700 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100701 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
702 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
703 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200704 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100705 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
706 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
707 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200708 }
709
Christopher Faulet8ef75252017-02-20 22:56:03 +0100710 vsn = spoe_str_to_vsn(str, sz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200711 if (vsn == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100712 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200713 return -1;
714 }
715 for (i = 0; supported_versions[i].str != NULL; ++i) {
716 if (vsn >= supported_versions[i].min &&
717 vsn <= supported_versions[i].max)
718 break;
719 }
720 if (supported_versions[i].str == NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100721 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200722 return -1;
723 }
724 }
725 /* Check "max-frame-size" K/V item */
726 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100727 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200728
729 /* The value must be integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200730 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
731 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
732 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
733 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100734 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
735 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200736 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200737 if (decode_varint(&p, end, &sz) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100738 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
739 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200740 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100741 if (sz < MIN_FRAME_SIZE ||
742 sz > SPOE_APPCTX(appctx)->max_frame_size) {
743 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200744 return -1;
745 }
746 max_frame_size = sz;
747 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100748 /* Check "capabilities" K/V item */
749 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100750 int type = *p++;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100751
752 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100753 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
754 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
755 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100756 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100757 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
758 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
759 return 0;
760 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100761
Christopher Faulet8ef75252017-02-20 22:56:03 +0100762 while (sz) {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100763 char *delim;
764
765 /* Skip leading spaces */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100766 for (; isspace(*str) && sz; str++, sz--);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100767
Christopher Faulet8ef75252017-02-20 22:56:03 +0100768 if (sz >= 10 && !strncmp(str, "pipelining", 10)) {
769 str += 10; sz -= 10;
770 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +0100771 flags |= SPOE_APPCTX_FL_PIPELINING;
772 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100773 else if (sz >= 5 && !strncmp(str, "async", 5)) {
774 str += 5; sz -= 5;
775 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +0100776 flags |= SPOE_APPCTX_FL_ASYNC;
777 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100778 else if (sz >= 13 && !strncmp(str, "fragmentation", 13)) {
779 str += 13; sz -= 13;
780 if (!sz || isspace(*str) || *str == ',')
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100781 flags |= SPOE_APPCTX_FL_FRAGMENTATION;
782 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100783
Christopher Faulet8ef75252017-02-20 22:56:03 +0100784 /* Get the next comma or break */
785 if (!sz || (delim = memchr(str, ',', sz)) == NULL)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100786 break;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100787 delim++;
788 sz -= (delim - str);
789 str = delim;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100790 }
791 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200792 else {
793 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100794 if (spoe_skip_data(&p, end) == -1) {
795 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
796 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200797 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200798 }
799 }
800
801 /* Final checks */
802 if (!vsn) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100803 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200804 return -1;
805 }
806 if (!max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100807 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200808 return -1;
809 }
Christopher Faulet305c6072017-02-23 16:17:53 +0100810 if ((flags & SPOE_APPCTX_FL_PIPELINING) && !(agent->flags & SPOE_FL_PIPELINING))
811 flags &= ~SPOE_APPCTX_FL_PIPELINING;
812 if ((flags & SPOE_APPCTX_FL_ASYNC) && !(agent->flags & SPOE_FL_ASYNC))
813 flags &= ~SPOE_APPCTX_FL_ASYNC;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200814
Christopher Faulet42bfa462017-01-04 14:14:19 +0100815 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
816 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
817 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100818
819 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200820}
821
822/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
823 * bytes on success, 0 if the frame can be ignored and -1 if an error
824 * occurred. */
825static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100826spoe_handle_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200827{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100828 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100829 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100830
831 p = frame;
832 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200833
834 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100835 if (*p++ != SPOE_FRM_T_AGENT_DISCON) {
836 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200837 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100838 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200839
Christopher Faulet8ef75252017-02-20 22:56:03 +0100840 if (size < 7 /* TYPE + METADATA */) {
841 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
842 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200843 }
844
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100845 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100846 memcpy((char *)&flags, p, 4);
847 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200848
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100849 /* Fragmentation is not supported for DISCONNECT frame */
850 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100851 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100852 return -1;
853 }
854
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200855 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100856 if (*p != 0 || *(p+1) != 0) {
857 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
858 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200859 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100860 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200861
862 /* There are 2 mandatory items: "status-code" and "message" */
863
864 /* Loop on K/V items */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100865 while (p < end) {
866 char *str;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200867 uint64_t sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100868 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200869
870 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100871 ret = spoe_decode_buffer(&p, end, &str, &sz);
872 if (ret == -1 || !sz) {
873 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
874 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200875 }
876
877 /* Check "status-code" K/V item */
878 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100879 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200880
881 /* The value must be an integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200882 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
883 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
884 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
885 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100886 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
887 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200888 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200889 if (decode_varint(&p, end, &sz) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100890 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
891 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200892 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100893 SPOE_APPCTX(appctx)->status_code = sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200894 }
895
896 /* Check "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100897 else if (!memcmp(str, MSG_KEY, sz)) {
898 int type = *p++;
899
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200900 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100901 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
902 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
903 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200904 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100905 ret = spoe_decode_buffer(&p, end, &str, &sz);
906 if (ret == -1 || sz > 255) {
907 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
908 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200909 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100910#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
911 SPOE_APPCTX(appctx)->reason = str;
912 SPOE_APPCTX(appctx)->rlen = sz;
913#endif
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200914 }
915 else {
916 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100917 if (spoe_skip_data(&p, end) == -1) {
918 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
919 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200920 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200921 }
922 }
923
Christopher Faulet8ef75252017-02-20 22:56:03 +0100924 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200925}
926
927
Christopher Fauleta1cda022016-12-21 08:58:06 +0100928/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200929 * success, 0 if the frame can be ignored and -1 if an error occurred. */
930static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100931spoe_handle_agentack_frame(struct appctx *appctx, struct spoe_context **ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100932 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200933{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100934 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100935 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100936 uint64_t stream_id, frame_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100937 int len;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100938 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100939
940 p = frame;
941 end = frame + size;
942 *ctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200943
944 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100945 if (*p++ != SPOE_FRM_T_AGENT_ACK) {
946 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200947 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100948 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200949
Christopher Faulet8ef75252017-02-20 22:56:03 +0100950 if (size < 7 /* TYPE + METADATA */) {
951 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
952 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200953 }
954
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100955 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100956 memcpy((char *)&flags, p, 4);
957 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200958
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100959 /* Fragmentation is not supported for now */
960 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100961 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100962 return -1;
963 }
964
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200965 /* Get the stream-id and the frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200966 if (decode_varint(&p, end, &stream_id) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100967 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100968 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100969 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200970 if (decode_varint(&p, end, &frame_id) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100971 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200972 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100973 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100974
Christopher Faulet8ef75252017-02-20 22:56:03 +0100975 /* Try to find the corresponding SPOE context */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100976 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100977 list_for_each_entry((*ctx), &agent->waiting_queue, list) {
978 if ((*ctx)->stream_id == (unsigned int)stream_id &&
979 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100980 goto found;
981 }
982 }
983 else {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100984 list_for_each_entry((*ctx), &SPOE_APPCTX(appctx)->waiting_queue, list) {
985 if ((*ctx)->stream_id == (unsigned int)stream_id &&
Christopher Faulet8ef75252017-02-20 22:56:03 +0100986 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100987 goto found;
988 }
989 }
990
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100991 if (SPOE_APPCTX(appctx)->frag_ctx.ctx &&
992 SPOE_APPCTX(appctx)->frag_ctx.cursid == (unsigned int)stream_id &&
993 SPOE_APPCTX(appctx)->frag_ctx.curfid == (unsigned int)frame_id) {
994
995 /* ABRT bit is set for an unfinished fragmented frame */
996 if (flags & SPOE_FRM_FL_ABRT) {
997 *ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
998 (*ctx)->frag_ctx.spoe_appctx = NULL;
999 (*ctx)->state = SPOE_CTX_ST_ERROR;
1000 (*ctx)->status_code = SPOE_CTX_ERR_FRAG_FRAME_ABRT;
1001 /* Ignore the payload */
1002 goto end;
1003 }
1004 /* TODO: Handle more flags for fragmented frames: RESUME, FINISH... */
1005 /* For now, we ignore the ack */
1006 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1007 return 0;
1008 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001009
Christopher Fauleta1cda022016-12-21 08:58:06 +01001010 /* No Stream found, ignore the frame */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001011 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1012 " - Ignore ACK frame"
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001013 " - stream-id=%u - frame-id=%u\n",
1014 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1015 __FUNCTION__, appctx,
1016 (unsigned int)stream_id, (unsigned int)frame_id);
1017
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001018 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAMEID_NOTFOUND;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001019 return 0;
1020
1021 found:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001022 if (!spoe_acquire_buffer(&SPOE_APPCTX(appctx)->buffer,
1023 &SPOE_APPCTX(appctx)->buffer_wait)) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001024 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001025 return 1; /* Retry later */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001026 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001027
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001028 /* Copy encoded actions */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001029 len = (end - p);
1030 memcpy(SPOE_APPCTX(appctx)->buffer->p, p, len);
1031 SPOE_APPCTX(appctx)->buffer->i = len;
1032 p += len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001033
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001034 /* Transfer the buffer ownership to the SPOE context */
1035 (*ctx)->buffer = SPOE_APPCTX(appctx)->buffer;
1036 SPOE_APPCTX(appctx)->buffer = &buf_empty;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001037
Christopher Faulet8ef75252017-02-20 22:56:03 +01001038 (*ctx)->state = SPOE_CTX_ST_DONE;
1039
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001040 end:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001041 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001042 " - ACK frame received"
1043 " - ctx=%p - stream-id=%u - frame-id=%u - flags=0x%08x\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001044 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001045 __FUNCTION__, appctx, *ctx, (*ctx)->stream_id,
1046 (*ctx)->frame_id, flags);
1047 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001048}
1049
Christopher Fauletba7bc162016-11-07 21:07:38 +01001050/* This function is used in cfgparse.c and declared in proto/checks.h. It
1051 * prepare the request to send to agents during a healthcheck. It returns 0 on
1052 * success and -1 if an error occurred. */
1053int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001054spoe_prepare_healthcheck_request(char **req, int *len)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001055{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001056 struct appctx appctx;
1057 struct spoe_appctx spoe_appctx;
1058 char *frame, *end, buf[MAX_FRAME_SIZE+4];
1059 size_t sz;
1060 int ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001061
Christopher Faulet42bfa462017-01-04 14:14:19 +01001062 memset(&appctx, 0, sizeof(appctx));
1063 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001064 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001065
1066 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001067 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001068
Christopher Faulet8ef75252017-02-20 22:56:03 +01001069 frame = buf+4; /* Reserved the 4 first bytes for the frame size */
1070 end = frame + MAX_FRAME_SIZE;
1071
1072 ret = spoe_prepare_hahello_frame(&appctx, frame, MAX_FRAME_SIZE);
1073 if (ret <= 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001074 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001075 frame += ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001076
Christopher Faulet8ef75252017-02-20 22:56:03 +01001077 /* Add "healthcheck" K/V item */
1078 sz = SLEN(HEALTHCHECK_KEY);
1079 if (spoe_encode_buffer(HEALTHCHECK_KEY, sz, &frame, end) == -1)
1080 return -1;
1081 *frame++ = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001082
Christopher Faulet8ef75252017-02-20 22:56:03 +01001083 *len = frame - buf;
1084 sz = htonl(*len - 4);
1085 memcpy(buf, (char *)&sz, 4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001086
Christopher Faulet8ef75252017-02-20 22:56:03 +01001087 if ((*req = malloc(*len)) == NULL)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001088 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001089 memcpy(*req, buf, *len);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001090 return 0;
1091}
1092
1093/* This function is used in checks.c and declared in proto/checks.h. It decode
1094 * the response received from an agent during a healthcheck. It returns 0 on
1095 * success and -1 if an error occurred. */
1096int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001097spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001098{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001099 struct appctx appctx;
1100 struct spoe_appctx spoe_appctx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001101
Christopher Faulet42bfa462017-01-04 14:14:19 +01001102 memset(&appctx, 0, sizeof(appctx));
1103 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001104
Christopher Faulet42bfa462017-01-04 14:14:19 +01001105 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001106 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001107
Christopher Faulet8ef75252017-02-20 22:56:03 +01001108 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1109 spoe_handle_agentdiscon_frame(&appctx, frame, size);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001110 goto error;
1111 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001112 if (spoe_handle_agenthello_frame(&appctx, frame, size) <= 0)
1113 goto error;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001114
1115 return 0;
1116
1117 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001118 if (SPOE_APPCTX(&appctx)->status_code >= SPOE_FRM_ERRS)
1119 SPOE_APPCTX(&appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
1120 strncpy(err, spoe_frm_err_reasons[SPOE_APPCTX(&appctx)->status_code], errlen);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001121 return -1;
1122}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001123
Christopher Fauleta1cda022016-12-21 08:58:06 +01001124/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1125 * the frame can be ignored, 1 to retry later, and the frame legnth on
1126 * success. */
1127static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001128spoe_send_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001129{
1130 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001131 int ret;
1132 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001133
1134 if (si_ic(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001135 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001136
Christopher Faulet8ef75252017-02-20 22:56:03 +01001137 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1138 * length. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001139 netint = htonl(framesz);
1140 memcpy(buf, (char *)&netint, 4);
Willy Tarreau06d80a92017-10-19 14:32:15 +02001141 ret = ci_putblk(si_ic(si), buf, framesz+4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001142
1143 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001144 if (ret == -1) {
1145 retry:
1146 si_applet_cant_put(si);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001147 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001148 }
1149 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001150 return -1; /* error */
1151 }
1152 return framesz;
1153}
1154
1155/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1156 * when the frame can be ignored, 1 to retry later and the frame length on
1157 * success. */
1158static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001159spoe_recv_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001160{
1161 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001162 int ret;
1163 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001164
1165 if (si_oc(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001166 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001167
Willy Tarreau06d80a92017-10-19 14:32:15 +02001168 ret = co_getblk(si_oc(si), (char *)&netint, 4, 0);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001169 if (ret > 0) {
1170 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001171 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001172 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001173 return -1;
1174 }
Willy Tarreau06d80a92017-10-19 14:32:15 +02001175 ret = co_getblk(si_oc(si), buf, framesz, 4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001176 }
1177 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001178 if (ret == 0) {
1179 retry:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001180 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001181 }
1182 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001183 return -1; /* error */
1184 }
1185 return framesz;
1186}
1187
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001188/********************************************************************
1189 * Functions that manage the SPOE applet
1190 ********************************************************************/
Christopher Faulet4596fb72017-01-11 14:05:19 +01001191static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001192spoe_wakeup_appctx(struct appctx *appctx)
Christopher Faulet4596fb72017-01-11 14:05:19 +01001193{
1194 si_applet_want_get(appctx->owner);
1195 si_applet_want_put(appctx->owner);
1196 appctx_wakeup(appctx);
1197 return 1;
1198}
1199
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001200/* Callback function that catches applet timeouts. If a timeout occurred, we set
1201 * <appctx->st1> flag and the SPOE applet is woken up. */
1202static struct task *
Christopher Faulet8ef75252017-02-20 22:56:03 +01001203spoe_process_appctx(struct task * task)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001204{
1205 struct appctx *appctx = task->context;
1206
1207 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1208 if (tick_is_expired(task->expire, now_ms)) {
1209 task->expire = TICK_ETERNITY;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001210 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001211 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001212 spoe_wakeup_appctx(appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001213 return task;
1214}
1215
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001216/* Callback function that releases a SPOE applet. This happens when the
1217 * connection with the agent is closed. */
1218static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001219spoe_release_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001220{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001221 struct stream_interface *si = appctx->owner;
1222 struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
1223 struct spoe_agent *agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001224 struct spoe_context *ctx, *back;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001225
1226 if (spoe_appctx == NULL)
1227 return;
1228
1229 appctx->ctx.spoe.ptr = NULL;
1230 agent = spoe_appctx->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001231
1232 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1233 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1234 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001235
Christopher Faulet8ef75252017-02-20 22:56:03 +01001236 /* Remove applet from the list of running applets */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001237 agent->applets_act--;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001238 if (!LIST_ISEMPTY(&spoe_appctx->list)) {
1239 LIST_DEL(&spoe_appctx->list);
1240 LIST_INIT(&spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001241 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001242
Christopher Faulet8ef75252017-02-20 22:56:03 +01001243 /* Shutdown the server connection, if needed */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001244 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001245 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1246 agent->applets_idle--;
1247
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001248 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001249 if (spoe_appctx->status_code == SPOE_FRM_ERR_NONE)
1250 spoe_appctx->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001251
1252 si_shutw(si);
1253 si_shutr(si);
1254 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001255 }
1256
Christopher Faulet8ef75252017-02-20 22:56:03 +01001257 /* Destroy the task attached to this applet */
1258 if (spoe_appctx->task) {
1259 task_delete(spoe_appctx->task);
1260 task_free(spoe_appctx->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001261 }
1262
Christopher Faulet8ef75252017-02-20 22:56:03 +01001263 /* Notify all waiting streams */
1264 list_for_each_entry_safe(ctx, back, &spoe_appctx->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001265 LIST_DEL(&ctx->list);
1266 LIST_INIT(&ctx->list);
1267 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001268 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001269 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001270 }
1271
Christopher Faulet8ef75252017-02-20 22:56:03 +01001272 /* If the applet was processing a fragmented frame, notify the
1273 * corresponding stream. */
1274 if (spoe_appctx->frag_ctx.ctx) {
1275 ctx = spoe_appctx->frag_ctx.ctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001276 ctx->frag_ctx.spoe_appctx = NULL;
1277 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001278 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001279 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1280 }
1281
Christopher Faulet8ef75252017-02-20 22:56:03 +01001282 /* Release allocated memory */
1283 spoe_release_buffer(&spoe_appctx->buffer,
1284 &spoe_appctx->buffer_wait);
1285 pool_free2(pool2_spoe_appctx, spoe_appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001286
Christopher Fauleta1cda022016-12-21 08:58:06 +01001287 if (!LIST_ISEMPTY(&agent->applets))
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001288 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001289
Christopher Faulet8ef75252017-02-20 22:56:03 +01001290 /* If this was the last running applet, notify all waiting streams */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001291 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1292 LIST_DEL(&ctx->list);
1293 LIST_INIT(&ctx->list);
1294 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001295 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001296 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001297 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001298 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1299 LIST_DEL(&ctx->list);
1300 LIST_INIT(&ctx->list);
1301 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001302 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001303 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1304 }
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001305
1306 end:
1307 /* Update runtinme agent info */
1308 agent->frame_size = agent->max_frame_size;
1309 list_for_each_entry(spoe_appctx, &agent->applets, list)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001310 agent->frame_size = MIN(spoe_appctx->max_frame_size,
1311 agent->frame_size);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001312}
1313
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001314static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001315spoe_handle_connect_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001316{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001317 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001318 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001319 char *frame, *buf;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001320 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001321
Christopher Fauleta1cda022016-12-21 08:58:06 +01001322 if (si->state <= SI_ST_CON) {
1323 si_applet_want_put(si);
1324 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1325 goto stop;
1326 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001327 if (si->state != SI_ST_EST) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001328 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001329 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001330 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001331
Christopher Fauleta1cda022016-12-21 08:58:06 +01001332 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001333 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1334 " - Connection timed out\n",
1335 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1336 __FUNCTION__, appctx);
1337 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001338 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001339 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001340
Christopher Faulet42bfa462017-01-04 14:14:19 +01001341 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001342 SPOE_APPCTX(appctx)->task->expire =
1343 tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001344
Christopher Faulet8ef75252017-02-20 22:56:03 +01001345 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1346 * length. */
1347 buf = trash.str; frame = buf+4;
1348 ret = spoe_prepare_hahello_frame(appctx, frame,
1349 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001350 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001351 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001352
1353 switch (ret) {
1354 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001355 case 0: /* ignore => an error, cannot be ignored */
1356 goto exit;
1357
1358 case 1: /* retry later */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001359 goto stop;
1360
Christopher Faulet8ef75252017-02-20 22:56:03 +01001361 default:
1362 /* HELLO frame successfully sent, now wait for the
1363 * reply. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001364 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1365 goto next;
1366 }
1367
1368 next:
1369 return 0;
1370 stop:
1371 return 1;
1372 exit:
1373 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1374 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001375}
1376
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001377static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001378spoe_handle_connecting_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001379{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001380 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001381 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001382 char *frame;
1383 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001384
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001385
Christopher Fauletb067b062017-01-04 16:39:11 +01001386 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001387 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001388 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001389 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001390
Christopher Fauleta1cda022016-12-21 08:58:06 +01001391 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001392 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1393 " - Connection timed out\n",
1394 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1395 __FUNCTION__, appctx);
1396 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001397 goto exit;
1398 }
1399
Christopher Faulet8ef75252017-02-20 22:56:03 +01001400 frame = trash.str; trash.len = 0;
1401 ret = spoe_recv_frame(appctx, frame,
1402 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001403 if (ret > 1) {
1404 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1405 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1406 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001407 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001408 trash.len = ret + 4;
1409 ret = spoe_handle_agenthello_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001410 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001411
Christopher Fauleta1cda022016-12-21 08:58:06 +01001412 switch (ret) {
1413 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001414 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001415 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1416 goto next;
1417
1418 case 1: /* retry later */
1419 goto stop;
1420
1421 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001422 /* HELLO handshake is finished, set the idle timeout and
1423 * add the applet in the list of running applets. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001424 agent->applets_idle++;
1425 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001426 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1427 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001428
1429 /* Update runtinme agent info */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001430 agent->frame_size = MIN(SPOE_APPCTX(appctx)->max_frame_size,
1431 agent->frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001432 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001433 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001434
Christopher Fauleta1cda022016-12-21 08:58:06 +01001435 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001436 /* Do not forget to remove processed frame from the output buffer */
1437 if (trash.len)
Willy Tarreau06d80a92017-10-19 14:32:15 +02001438 co_skip(si_oc(si), trash.len);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001439
1440 SPOE_APPCTX(appctx)->task->expire =
1441 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001442 return 0;
1443 stop:
1444 return 1;
1445 exit:
1446 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1447 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001448}
1449
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001450
Christopher Fauleta1cda022016-12-21 08:58:06 +01001451static int
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001452spoe_handle_sending_frame_appctx(struct appctx *appctx, int *skip)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001453{
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001454 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
1455 struct spoe_context *ctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001456 char *frame, *buf;
1457 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001458
Christopher Faulet8ef75252017-02-20 22:56:03 +01001459 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1460 * length. */
1461 buf = trash.str; frame = buf+4;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001462
1463 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY) {
1464 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1465 ret = spoe_prepare_hafrag_frame(appctx, ctx, frame,
1466 SPOE_APPCTX(appctx)->max_frame_size);
1467 }
1468 else if (LIST_ISEMPTY(&agent->sending_queue)) {
1469 *skip = 1;
1470 ret = 1;
1471 goto end;
1472 }
1473 else {
1474 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
1475 ret = spoe_prepare_hanotify_frame(appctx, ctx, frame,
1476 SPOE_APPCTX(appctx)->max_frame_size);
1477
1478 }
1479
Christopher Faulet8ef75252017-02-20 22:56:03 +01001480 if (ret > 1)
1481 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001482
Christopher Faulet8ef75252017-02-20 22:56:03 +01001483 switch (ret) {
1484 case -1: /* error */
1485 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1486 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001487
Christopher Faulet8ef75252017-02-20 22:56:03 +01001488 case 0: /* ignore */
1489 if (ctx == NULL)
1490 goto abort_frag_frame;
1491
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001492 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001493 LIST_DEL(&ctx->list);
1494 LIST_INIT(&ctx->list);
1495 ctx->state = SPOE_CTX_ST_ERROR;
1496 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
1497 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1498 break;
1499
1500 case 1: /* retry */
1501 *skip = 1;
1502 break;
1503
1504 default:
1505 if (ctx == NULL)
1506 goto abort_frag_frame;
1507
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001508 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001509 LIST_DEL(&ctx->list);
1510 LIST_INIT(&ctx->list);
1511 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) ||
1512 (ctx->frag_ctx.flags & SPOE_FRM_FL_FIN))
1513 goto no_frag_frame_sent;
1514 else {
1515 *skip = 1;
1516 goto frag_frame_sent;
1517 }
1518 }
1519 goto end;
1520
1521 frag_frame_sent:
1522 appctx->st0 = SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY;
1523 SPOE_APPCTX(appctx)->frag_ctx.ctx = ctx;
1524 SPOE_APPCTX(appctx)->frag_ctx.cursid = ctx->stream_id;
1525 SPOE_APPCTX(appctx)->frag_ctx.curfid = ctx->frame_id;
1526
1527 ctx->frag_ctx.spoe_appctx = SPOE_APPCTX(appctx);
1528 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
1529 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1530 goto end;
1531
1532 no_frag_frame_sent:
1533 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
1534 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1535 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1536 }
1537 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
1538 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1539 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1540 }
1541 else {
1542 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
1543 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1544 }
1545 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1546 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1547 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1548
1549 ctx->frag_ctx.spoe_appctx = NULL;
1550 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1551 goto end;
1552
1553 abort_frag_frame:
1554 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1555 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1556 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1557 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1558 goto end;
1559
1560 end:
1561 return ret;
1562}
1563
1564static int
1565spoe_handle_receiving_frame_appctx(struct appctx *appctx, int *skip)
1566{
1567 struct spoe_context *ctx = NULL;
1568 char *frame;
1569 int ret;
1570
1571 frame = trash.str; trash.len = 0;
1572 ret = spoe_recv_frame(appctx, frame,
1573 SPOE_APPCTX(appctx)->max_frame_size);
1574 if (ret > 1) {
1575 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1576 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1577 goto end;
1578 }
1579 trash.len = ret + 4;
1580 ret = spoe_handle_agentack_frame(appctx, &ctx, frame, ret);
1581 }
1582 switch (ret) {
1583 case -1: /* error */
1584 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1585 break;
1586
1587 case 0: /* ignore */
1588 break;
1589
1590 case 1: /* retry */
1591 *skip = 1;
1592 break;
1593
1594 default:
1595 LIST_DEL(&ctx->list);
1596 LIST_INIT(&ctx->list);
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001597
1598 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY &&
1599 ctx == SPOE_APPCTX(appctx)->frag_ctx.ctx) {
1600 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1601 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1602 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1603 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1604 }
1605 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1606 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1607
Christopher Faulet8ef75252017-02-20 22:56:03 +01001608 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1609 break;
1610 }
1611
1612 /* Do not forget to remove processed frame from the output buffer */
1613 if (trash.len)
Willy Tarreau06d80a92017-10-19 14:32:15 +02001614 co_skip(si_oc(appctx->owner), trash.len);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001615 end:
1616 return ret;
1617}
1618
1619static int
1620spoe_handle_processing_appctx(struct appctx *appctx)
1621{
1622 struct stream_interface *si = appctx->owner;
1623 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001624 unsigned int fpa = 0;
1625 int ret, skip_sending = 0, skip_receiving = 0;
1626
1627 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1628 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
1629 goto exit;
1630 }
1631
1632 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1633 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
1634 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1635 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1636 goto next;
1637 }
1638
1639 process:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001640 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1641 " - process: fpa=%u/%u - skip_sending=%d - skip_receiving=%d"
1642 " - appctx-state=%s\n",
1643 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1644 __FUNCTION__, appctx, fpa, agent->max_fpa,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001645 skip_sending, skip_receiving,
1646 spoe_appctx_state_str[appctx->st0]);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001647
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001648 if (fpa > agent->max_fpa)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001649 goto stop;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001650 else if (skip_sending || appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001651 if (skip_receiving)
1652 goto stop;
1653 goto recv_frame;
1654 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001655
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001656 /* send_frame */
1657 ret = spoe_handle_sending_frame_appctx(appctx, &skip_sending);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001658 switch (ret) {
1659 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001660 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001661
Christopher Fauleta1cda022016-12-21 08:58:06 +01001662 case 0: /* ignore */
1663 agent->sending_rate++;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001664 fpa++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001665 break;
1666
Christopher Fauleta1cda022016-12-21 08:58:06 +01001667 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001668 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001669
Christopher Fauleta1cda022016-12-21 08:58:06 +01001670 default:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001671 agent->sending_rate++;
1672 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001673 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001674 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001675 if (fpa > agent->max_fpa)
1676 goto stop;
1677
1678 recv_frame:
1679 if (skip_receiving)
1680 goto process;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001681 ret = spoe_handle_receiving_frame_appctx(appctx, &skip_receiving);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001682 switch (ret) {
1683 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001684 goto next;
1685
1686 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001687 fpa++;
1688 break;
1689
1690 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001691 break;
1692
1693 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001694 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001695 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001696 }
1697 goto process;
1698
1699 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001700 SPOE_APPCTX(appctx)->task->expire =
1701 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001702 return 0;
1703 stop:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001704 if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001705 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001706 agent->applets_idle++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001707 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01001708 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
1709 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1710 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001711 if (fpa)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001712 SPOE_APPCTX(appctx)->task->expire =
1713 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001714 }
1715 return 1;
1716
1717 exit:
1718 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1719 return 0;
1720}
1721
1722static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001723spoe_handle_disconnect_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001724{
1725 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001726 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001727 char *frame, *buf;
1728 int ret;
Christopher Fauletb067b062017-01-04 16:39:11 +01001729
Christopher Fauleta1cda022016-12-21 08:58:06 +01001730 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
1731 goto exit;
1732
1733 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
1734 goto exit;
1735
Christopher Faulet8ef75252017-02-20 22:56:03 +01001736 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1737 * length. */
1738 buf = trash.str; frame = buf+4;
1739 ret = spoe_prepare_hadiscon_frame(appctx, frame,
1740 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001741 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001742 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001743
1744 switch (ret) {
1745 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001746 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001747 goto exit;
1748
1749 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001750 goto stop;
1751
1752 default:
1753 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1754 " - disconnected by HAProxy (%d): %s\n",
1755 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001756 __FUNCTION__, appctx,
1757 SPOE_APPCTX(appctx)->status_code,
1758 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001759
1760 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1761 goto next;
1762 }
1763
1764 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001765 SPOE_APPCTX(appctx)->task->expire =
1766 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001767 return 0;
1768 stop:
1769 return 1;
1770 exit:
1771 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1772 return 0;
1773}
1774
1775static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001776spoe_handle_disconnecting_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001777{
1778 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001779 char *frame;
1780 int ret;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001781
Christopher Fauletb067b062017-01-04 16:39:11 +01001782 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001783 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001784 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001785 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001786
Christopher Fauletb067b062017-01-04 16:39:11 +01001787 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001788 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001789 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001790 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001791
Christopher Faulet8ef75252017-02-20 22:56:03 +01001792 frame = trash.str; trash.len = 0;
1793 ret = spoe_recv_frame(appctx, frame,
1794 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001795 if (ret > 1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001796 trash.len = ret + 4;
1797 ret = spoe_handle_agentdiscon_frame(appctx, frame, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001798 }
1799
1800 switch (ret) {
1801 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001802 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1803 " - error on frame (%s)\n",
1804 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001805 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001806 __FUNCTION__, appctx,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001807 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001808 goto exit;
1809
1810 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001811 goto next;
1812
1813 case 1: /* retry */
1814 goto stop;
1815
1816 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001817 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001818 " - disconnected by peer (%d): %.*s\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01001819 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001820 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001821 __FUNCTION__, appctx, SPOE_APPCTX(appctx)->status_code,
1822 SPOE_APPCTX(appctx)->rlen, SPOE_APPCTX(appctx)->reason);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001823 goto exit;
1824 }
1825
1826 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001827 /* Do not forget to remove processed frame from the output buffer */
1828 if (trash.len)
Willy Tarreau06d80a92017-10-19 14:32:15 +02001829 co_skip(si_oc(appctx->owner), trash.len);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001830
Christopher Fauleta1cda022016-12-21 08:58:06 +01001831 return 0;
1832 stop:
1833 return 1;
1834 exit:
1835 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1836 return 0;
1837}
1838
1839/* I/O Handler processing messages exchanged with the agent */
1840static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001841spoe_handle_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001842{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001843 struct stream_interface *si = appctx->owner;
1844 struct spoe_agent *agent;
1845
1846 if (SPOE_APPCTX(appctx) == NULL)
1847 return;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001848
Christopher Faulet8ef75252017-02-20 22:56:03 +01001849 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
1850 agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletb067b062017-01-04 16:39:11 +01001851
Christopher Fauleta1cda022016-12-21 08:58:06 +01001852 switchstate:
1853 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1854 " - appctx-state=%s\n",
1855 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1856 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
1857
1858 switch (appctx->st0) {
1859 case SPOE_APPCTX_ST_CONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001860 if (spoe_handle_connect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001861 goto out;
1862 goto switchstate;
1863
1864 case SPOE_APPCTX_ST_CONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001865 if (spoe_handle_connecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001866 goto out;
1867 goto switchstate;
1868
1869 case SPOE_APPCTX_ST_IDLE:
1870 if (stopping &&
1871 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01001872 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001873 SPOE_APPCTX(appctx)->task->expire =
1874 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001875 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001876 goto switchstate;
1877 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001878 agent->applets_idle--;
1879 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1880 /* fall through */
1881
1882 case SPOE_APPCTX_ST_PROCESSING:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001883 case SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY:
1884 case SPOE_APPCTX_ST_WAITING_SYNC_ACK:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001885 if (spoe_handle_processing_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001886 goto out;
1887 goto switchstate;
1888
1889 case SPOE_APPCTX_ST_DISCONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001890 if (spoe_handle_disconnect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001891 goto out;
1892 goto switchstate;
1893
1894 case SPOE_APPCTX_ST_DISCONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001895 if (spoe_handle_disconnecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001896 goto out;
1897 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001898
1899 case SPOE_APPCTX_ST_EXIT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001900 appctx->st0 = SPOE_APPCTX_ST_END;
1901 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
1902
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001903 si_shutw(si);
1904 si_shutr(si);
1905 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001906 /* fall through */
1907
1908 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001909 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001910 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001911 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001912 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
1913 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001914 si_oc(si)->flags |= CF_READ_DONTWAIT;
1915 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001916}
1917
1918struct applet spoe_applet = {
1919 .obj_type = OBJ_TYPE_APPLET,
1920 .name = "<SPOE>", /* used for logging */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001921 .fct = spoe_handle_appctx,
1922 .release = spoe_release_appctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001923};
1924
1925/* Create a SPOE applet. On success, the created applet is returned, else
1926 * NULL. */
1927static struct appctx *
Christopher Faulet8ef75252017-02-20 22:56:03 +01001928spoe_create_appctx(struct spoe_config *conf)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001929{
1930 struct appctx *appctx;
1931 struct session *sess;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001932 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001933
Emeric Brun1138fd02017-06-19 12:38:55 +02001934 if ((appctx = appctx_new(&spoe_applet, tid_bit)) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001935 goto out_error;
1936
Christopher Faulet42bfa462017-01-04 14:14:19 +01001937 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
1938 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001939 goto out_free_appctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001940 memset(appctx->ctx.spoe.ptr, 0, pool2_spoe_appctx->size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001941
Christopher Faulet42bfa462017-01-04 14:14:19 +01001942 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
Emeric Brunc60def82017-09-27 14:59:38 +02001943 if ((SPOE_APPCTX(appctx)->task = task_new(MAX_THREADS_MASK)) == NULL)
Christopher Faulet42bfa462017-01-04 14:14:19 +01001944 goto out_free_spoe_appctx;
1945
1946 SPOE_APPCTX(appctx)->owner = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001947 SPOE_APPCTX(appctx)->task->process = spoe_process_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001948 SPOE_APPCTX(appctx)->task->context = appctx;
1949 SPOE_APPCTX(appctx)->agent = conf->agent;
1950 SPOE_APPCTX(appctx)->version = 0;
1951 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
1952 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01001953 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet4596fb72017-01-11 14:05:19 +01001954 SPOE_APPCTX(appctx)->buffer = &buf_empty;
1955
1956 LIST_INIT(&SPOE_APPCTX(appctx)->buffer_wait.list);
1957 SPOE_APPCTX(appctx)->buffer_wait.target = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001958 SPOE_APPCTX(appctx)->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001959
1960 LIST_INIT(&SPOE_APPCTX(appctx)->list);
1961 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001962
Willy Tarreau5820a362016-12-22 15:59:02 +01001963 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001964 if (!sess)
1965 goto out_free_spoe;
1966
Willy Tarreau87787ac2017-08-28 16:22:54 +02001967 if ((strm = stream_new(sess, &appctx->obj_type)) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001968 goto out_free_sess;
1969
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001970 stream_set_backend(strm, conf->agent->b.be);
1971
1972 /* applet is waiting for data */
1973 si_applet_cant_get(&strm->si[0]);
1974 appctx_wakeup(appctx);
1975
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001976 strm->do_log = NULL;
1977 strm->res.flags |= CF_READ_DONTWAIT;
1978
Christopher Faulet42bfa462017-01-04 14:14:19 +01001979 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001980 conf->agent->applets_act++;
Emeric Brun5f77fef2017-05-29 15:26:51 +02001981
Emeric Brunc60def82017-09-27 14:59:38 +02001982 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
Willy Tarreau87787ac2017-08-28 16:22:54 +02001983 task_wakeup(strm->task, TASK_WOKEN_INIT);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001984 return appctx;
1985
1986 /* Error unrolling */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001987 out_free_sess:
1988 session_free(sess);
1989 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001990 task_free(SPOE_APPCTX(appctx)->task);
1991 out_free_spoe_appctx:
1992 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001993 out_free_appctx:
1994 appctx_free(appctx);
1995 out_error:
1996 return NULL;
1997}
1998
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001999static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002000spoe_queue_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002001{
2002 struct spoe_config *conf = FLT_CONF(ctx->filter);
2003 struct spoe_agent *agent = conf->agent;
2004 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002005 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002006 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002007
Christopher Fauleta1cda022016-12-21 08:58:06 +01002008 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002009
Christopher Fauleta1cda022016-12-21 08:58:06 +01002010 /* Check if we need to create a new SPOE applet or not. */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002011 if (agent->applets_act >= min_applets &&
2012 agent->applets_idle &&
2013 agent->sending_rate)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002014 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002015
2016 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002017 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002018 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2019 ctx->strm);
2020
Christopher Fauleta1cda022016-12-21 08:58:06 +01002021 /* Do not try to create a new applet if there is no server up for the
2022 * agent's backend. */
2023 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2024 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2025 " - cannot create SPOE appctx: no server up\n",
2026 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2027 __FUNCTION__, ctx->strm);
2028 goto end;
2029 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002030
Christopher Fauleta1cda022016-12-21 08:58:06 +01002031 /* Do not try to create a new applet if we have reached the maximum of
2032 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002033 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002034 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2035 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2036 " - cannot create SPOE appctx: max CPS reached\n",
2037 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2038 __FUNCTION__, ctx->strm);
2039 goto end;
2040 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002041 }
2042
Christopher Faulet8ef75252017-02-20 22:56:03 +01002043 appctx = spoe_create_appctx(conf);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002044 if (appctx == NULL) {
2045 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2046 " - failed to create SPOE appctx\n",
2047 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2048 __FUNCTION__, ctx->strm);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002049 send_log(ctx->strm->be, LOG_EMERG,
2050 "SPOE: [%s] failed to create SPOE applet\n",
2051 agent->id);
2052
Christopher Fauleta1cda022016-12-21 08:58:06 +01002053 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002054 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002055 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002056 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002057
Christopher Fauleta1cda022016-12-21 08:58:06 +01002058 /* Increase the per-process number of cumulated connections */
2059 if (agent->cps_max > 0)
2060 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002061
Christopher Fauleta1cda022016-12-21 08:58:06 +01002062 end:
2063 /* The only reason to return an error is when there is no applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002064 if (LIST_ISEMPTY(&agent->applets)) {
2065 ctx->status_code = SPOE_CTX_ERR_RES;
2066 return -1;
2067 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002068
Christopher Fauleta1cda022016-12-21 08:58:06 +01002069 /* Add the SPOE context in the sending queue and update all running
2070 * info */
2071 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2072 if (agent->sending_rate)
2073 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002074
2075 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002076 " - Add stream in sending queue"
2077 " - applets_act=%u - applets_idle=%u - sending_rate=%u\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002078 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002079 ctx->strm, agent->applets_act, agent->applets_idle,
2080 agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002081
Christopher Fauleta1cda022016-12-21 08:58:06 +01002082 /* Finally try to wakeup the first IDLE applet found and move it at the
2083 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002084 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2085 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002086 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002087 spoe_wakeup_appctx(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002088 LIST_DEL(&spoe_appctx->list);
2089 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002090 break;
2091 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002092 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002093 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002094}
2095
2096/***************************************************************************
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002097 * Functions that encode SPOE messages
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002098 **************************************************************************/
Christopher Faulet10e37672017-09-21 16:38:22 +02002099/* Encode a SPOE message. Info in <ctx->frag_ctx>, if any, are used to handle
2100 * fragmented_content. If the next message can be processed, it returns 0. If
2101 * the message is too big, it returns -1.*/
2102static int
2103spoe_encode_message(struct stream *s, struct spoe_context *ctx,
2104 struct spoe_message *msg, int dir,
2105 char **buf, char *end)
2106{
2107 struct sample *smp;
2108 struct spoe_arg *arg;
2109 int ret;
2110
2111 if (msg->cond) {
2112 ret = acl_exec_cond(msg->cond, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2113 ret = acl_pass(ret);
2114 if (msg->cond->pol == ACL_COND_UNLESS)
2115 ret = !ret;
2116
2117 /* the rule does not match */
2118 if (!ret)
2119 goto next;
2120 }
2121
2122 /* Resume encoding of a SPOE argument */
2123 if (ctx->frag_ctx.curarg != NULL) {
2124 arg = ctx->frag_ctx.curarg;
2125 goto encode_argument;
2126 }
2127
2128 if (ctx->frag_ctx.curoff != UINT_MAX)
2129 goto encode_msg_payload;
2130
2131 /* Check if there is enough space for the message name and the
2132 * number of arguments. It implies <msg->id_len> is encoded on 2
2133 * bytes, at most (< 2288). */
2134 if (*buf + 2 + msg->id_len + 1 > end)
2135 goto too_big;
2136
2137 /* Encode the message name */
2138 if (spoe_encode_buffer(msg->id, msg->id_len, buf, end) == -1)
2139 goto too_big;
2140
2141 /* Set the number of arguments for this message */
2142 **buf = msg->nargs;
2143 (*buf)++;
2144
2145 ctx->frag_ctx.curoff = 0;
2146 encode_msg_payload:
2147
2148 /* Loop on arguments */
2149 list_for_each_entry(arg, &msg->args, list) {
2150 ctx->frag_ctx.curarg = arg;
2151 ctx->frag_ctx.curoff = UINT_MAX;
2152
2153 encode_argument:
2154 if (ctx->frag_ctx.curoff != UINT_MAX)
2155 goto encode_arg_value;
2156
2157 /* Encode the arguement name as a string. It can by NULL */
2158 if (spoe_encode_buffer(arg->name, arg->name_len, buf, end) == -1)
2159 goto too_big;
2160
2161 ctx->frag_ctx.curoff = 0;
2162 encode_arg_value:
2163
2164 /* Fetch the arguement value */
2165 smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
2166 ret = spoe_encode_data(smp, &ctx->frag_ctx.curoff, buf, end);
2167 if (ret == -1 || ctx->frag_ctx.curoff)
2168 goto too_big;
2169 }
2170
2171 next:
2172 return 0;
2173
2174 too_big:
2175 return -1;
2176}
2177
Christopher Fauletc718b822017-09-21 16:50:56 +02002178/* Encode list of SPOE messages. Info in <ctx->frag_ctx>, if any, are used to
2179 * handle fragmented content. On success it returns 1. If an error occurred, -1
2180 * is returned. If nothing has been encoded, it returns 0 (this is only possible
2181 * for unfragmented payload). */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002182static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002183spoe_encode_messages(struct stream *s, struct spoe_context *ctx,
Christopher Fauletc718b822017-09-21 16:50:56 +02002184 struct list *messages, int dir, int type)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002185{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002186 struct spoe_config *conf = FLT_CONF(ctx->filter);
2187 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002188 struct spoe_message *msg;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002189 char *p, *end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002190
Christopher Faulet8ef75252017-02-20 22:56:03 +01002191 p = ctx->buffer->p;
2192 end = p + agent->frame_size - FRAME_HDR_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002193
Christopher Fauletc718b822017-09-21 16:50:56 +02002194 if (type == SPOE_MSGS_BY_EVENT) { /* Loop on messages by event */
2195 /* Resume encoding of a SPOE message */
2196 if (ctx->frag_ctx.curmsg != NULL) {
2197 msg = ctx->frag_ctx.curmsg;
2198 goto encode_evt_message;
2199 }
2200
2201 list_for_each_entry(msg, messages, by_evt) {
2202 ctx->frag_ctx.curmsg = msg;
2203 ctx->frag_ctx.curarg = NULL;
2204 ctx->frag_ctx.curoff = UINT_MAX;
2205
2206 encode_evt_message:
2207 if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
2208 goto too_big;
2209 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002210 }
Christopher Fauletc718b822017-09-21 16:50:56 +02002211 else if (type == SPOE_MSGS_BY_GROUP) { /* Loop on messages by group */
2212 /* Resume encoding of a SPOE message */
2213 if (ctx->frag_ctx.curmsg != NULL) {
2214 msg = ctx->frag_ctx.curmsg;
2215 goto encode_grp_message;
2216 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002217
Christopher Fauletc718b822017-09-21 16:50:56 +02002218 list_for_each_entry(msg, messages, by_grp) {
2219 ctx->frag_ctx.curmsg = msg;
2220 ctx->frag_ctx.curarg = NULL;
2221 ctx->frag_ctx.curoff = UINT_MAX;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002222
Christopher Fauletc718b822017-09-21 16:50:56 +02002223 encode_grp_message:
2224 if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
2225 goto too_big;
2226 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002227 }
Christopher Fauletc718b822017-09-21 16:50:56 +02002228 else
2229 goto skip;
2230
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002231
Christopher Faulet57583e42017-09-04 15:41:09 +02002232 /* nothing has been encoded for an unfragmented payload */
2233 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) && p == ctx->buffer->p)
2234 goto skip;
2235
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002236 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002237 " - encode %s messages - spoe_appctx=%p"
2238 "- max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002239 (int)now.tv_sec, (int)now.tv_usec,
2240 agent->id, __FUNCTION__, s,
2241 ((ctx->flags & SPOE_CTX_FL_FRAGMENTED) ? "last fragment of" : "unfragmented"),
Christopher Faulet8ef75252017-02-20 22:56:03 +01002242 ctx->frag_ctx.spoe_appctx, (agent->frame_size - FRAME_HDR_SIZE),
2243 p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002244
Christopher Faulet8ef75252017-02-20 22:56:03 +01002245 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002246 ctx->frag_ctx.curmsg = NULL;
2247 ctx->frag_ctx.curarg = NULL;
2248 ctx->frag_ctx.curoff = 0;
2249 ctx->frag_ctx.flags = SPOE_FRM_FL_FIN;
Christopher Faulet57583e42017-09-04 15:41:09 +02002250
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002251 return 1;
2252
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002253 too_big:
Christopher Fauletcecd8522017-02-24 22:11:21 +01002254 if (!(agent->flags & SPOE_FL_SND_FRAGMENTATION)) {
2255 ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
2256 return -1;
2257 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002258
2259 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002260 " - encode fragmented messages - spoe_appctx=%p"
2261 " - curmsg=%p - curarg=%p - curoff=%u"
2262 " - max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002263 (int)now.tv_sec, (int)now.tv_usec,
2264 agent->id, __FUNCTION__, s, ctx->frag_ctx.spoe_appctx,
2265 ctx->frag_ctx.curmsg, ctx->frag_ctx.curarg, ctx->frag_ctx.curoff,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002266 (agent->frame_size - FRAME_HDR_SIZE), p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002267
Christopher Faulet8ef75252017-02-20 22:56:03 +01002268 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002269 ctx->flags |= SPOE_CTX_FL_FRAGMENTED;
2270 ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN;
2271 return 1;
Christopher Faulet57583e42017-09-04 15:41:09 +02002272
2273 skip:
2274 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2275 " - skip the frame because nothing has been encoded\n",
2276 (int)now.tv_sec, (int)now.tv_usec,
2277 agent->id, __FUNCTION__, s);
2278 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002279}
2280
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002281
2282/***************************************************************************
2283 * Functions that handle SPOE actions
2284 **************************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002285/* Helper function to set a variable */
2286static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002287spoe_set_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002288 struct sample *smp)
2289{
2290 struct spoe_config *conf = FLT_CONF(ctx->filter);
2291 struct spoe_agent *agent = conf->agent;
2292 char varname[64];
2293
2294 memset(varname, 0, sizeof(varname));
2295 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2296 scope, agent->var_pfx, len, name);
2297 vars_set_by_name_ifexist(varname, len, smp);
2298}
2299
2300/* Helper function to unset a variable */
2301static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002302spoe_unset_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002303 struct sample *smp)
2304{
2305 struct spoe_config *conf = FLT_CONF(ctx->filter);
2306 struct spoe_agent *agent = conf->agent;
2307 char varname[64];
2308
2309 memset(varname, 0, sizeof(varname));
2310 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2311 scope, agent->var_pfx, len, name);
2312 vars_unset_by_name_ifexist(varname, len, smp);
2313}
2314
2315
Christopher Faulet8ef75252017-02-20 22:56:03 +01002316static inline int
2317spoe_decode_action_set_var(struct stream *s, struct spoe_context *ctx,
2318 char **buf, char *end, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002319{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002320 char *str, *scope, *p = *buf;
2321 struct sample smp;
2322 uint64_t sz;
2323 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002324
Christopher Faulet8ef75252017-02-20 22:56:03 +01002325 if (p + 2 >= end)
2326 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002327
Christopher Faulet8ef75252017-02-20 22:56:03 +01002328 /* SET-VAR requires 3 arguments */
2329 if (*p++ != 3)
2330 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002331
Christopher Faulet8ef75252017-02-20 22:56:03 +01002332 switch (*p++) {
2333 case SPOE_SCOPE_PROC: scope = "proc"; break;
2334 case SPOE_SCOPE_SESS: scope = "sess"; break;
2335 case SPOE_SCOPE_TXN : scope = "txn"; break;
2336 case SPOE_SCOPE_REQ : scope = "req"; break;
2337 case SPOE_SCOPE_RES : scope = "res"; break;
2338 default: goto skip;
2339 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002340
Christopher Faulet8ef75252017-02-20 22:56:03 +01002341 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2342 goto skip;
2343 memset(&smp, 0, sizeof(smp));
2344 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002345
Christopher Faulet8ef75252017-02-20 22:56:03 +01002346 if (spoe_decode_data(&p, end, &smp) == -1)
2347 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002348
Christopher Faulet8ef75252017-02-20 22:56:03 +01002349 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2350 " - set-var '%s.%s.%.*s'\n",
2351 (int)now.tv_sec, (int)now.tv_usec,
2352 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2353 __FUNCTION__, s, scope,
2354 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2355 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002356
Christopher Faulet8ef75252017-02-20 22:56:03 +01002357 spoe_set_var(ctx, scope, str, sz, &smp);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002358
Christopher Faulet8ef75252017-02-20 22:56:03 +01002359 ret = (p - *buf);
2360 *buf = p;
2361 return ret;
2362 skip:
2363 return 0;
2364}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002365
Christopher Faulet8ef75252017-02-20 22:56:03 +01002366static inline int
2367spoe_decode_action_unset_var(struct stream *s, struct spoe_context *ctx,
2368 char **buf, char *end, int dir)
2369{
2370 char *str, *scope, *p = *buf;
2371 struct sample smp;
2372 uint64_t sz;
2373 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002374
Christopher Faulet8ef75252017-02-20 22:56:03 +01002375 if (p + 2 >= end)
2376 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002377
Christopher Faulet8ef75252017-02-20 22:56:03 +01002378 /* UNSET-VAR requires 2 arguments */
2379 if (*p++ != 2)
2380 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002381
Christopher Faulet8ef75252017-02-20 22:56:03 +01002382 switch (*p++) {
2383 case SPOE_SCOPE_PROC: scope = "proc"; break;
2384 case SPOE_SCOPE_SESS: scope = "sess"; break;
2385 case SPOE_SCOPE_TXN : scope = "txn"; break;
2386 case SPOE_SCOPE_REQ : scope = "req"; break;
2387 case SPOE_SCOPE_RES : scope = "res"; break;
2388 default: goto skip;
2389 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002390
Christopher Faulet8ef75252017-02-20 22:56:03 +01002391 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2392 goto skip;
2393 memset(&smp, 0, sizeof(smp));
2394 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002395
Christopher Faulet8ef75252017-02-20 22:56:03 +01002396 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2397 " - unset-var '%s.%s.%.*s'\n",
2398 (int)now.tv_sec, (int)now.tv_usec,
2399 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2400 __FUNCTION__, s, scope,
2401 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2402 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002403
Christopher Faulet8ef75252017-02-20 22:56:03 +01002404 spoe_unset_var(ctx, scope, str, sz, &smp);
2405
2406 ret = (p - *buf);
2407 *buf = p;
2408 return ret;
2409 skip:
2410 return 0;
2411}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002412
Christopher Faulet8ef75252017-02-20 22:56:03 +01002413/* Process SPOE actions for a specific event. It returns 1 on success. If an
2414 * error occurred, 0 is returned. */
2415static int
Christopher Faulet58d03682017-09-21 16:57:24 +02002416spoe_process_actions(struct stream *s, struct spoe_context *ctx, int dir)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002417{
2418 char *p, *end;
2419 int ret;
2420
2421 p = ctx->buffer->p;
2422 end = p + ctx->buffer->i;
2423
2424 while (p < end) {
2425 enum spoe_action_type type;
2426
2427 type = *p++;
2428 switch (type) {
2429 case SPOE_ACT_T_SET_VAR:
2430 ret = spoe_decode_action_set_var(s, ctx, &p, end, dir);
2431 if (!ret)
2432 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002433 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002434
Christopher Faulet8ef75252017-02-20 22:56:03 +01002435 case SPOE_ACT_T_UNSET_VAR:
2436 ret = spoe_decode_action_unset_var(s, ctx, &p, end, dir);
2437 if (!ret)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002438 goto skip;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002439 break;
2440
2441 default:
2442 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002443 }
2444 }
2445
2446 return 1;
2447 skip:
2448 return 0;
2449}
2450
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002451/***************************************************************************
2452 * Functions that process SPOE events
2453 **************************************************************************/
2454static inline int
Christopher Faulet58d03682017-09-21 16:57:24 +02002455spoe_start_processing(struct spoe_context *ctx, int dir)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002456{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002457 /* If a process is already started for this SPOE context, retry
2458 * later. */
2459 if (ctx->flags & SPOE_CTX_FL_PROCESS)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002460 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002461
2462 /* Set the right flag to prevent request and response processing
2463 * in same time. */
2464 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2465 ? SPOE_CTX_FL_REQ_PROCESS
2466 : SPOE_CTX_FL_RSP_PROCESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002467 return 1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002468}
2469
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002470static inline void
Christopher Faulet58d03682017-09-21 16:57:24 +02002471spoe_stop_processing(struct spoe_context *ctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002472{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002473 struct spoe_appctx *sa = ctx->frag_ctx.spoe_appctx;
2474
2475 if (sa) {
2476 sa->frag_ctx.ctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002477 spoe_wakeup_appctx(sa->owner);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002478 }
2479
Christopher Fauleta1cda022016-12-21 08:58:06 +01002480 /* Reset the flag to allow next processing */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002481 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002482
Christopher Fauletb067b062017-01-04 16:39:11 +01002483 ctx->status_code = 0;
2484
Christopher Fauleta1cda022016-12-21 08:58:06 +01002485 /* Reset processing timer */
2486 ctx->process_exp = TICK_ETERNITY;
2487
Christopher Faulet8ef75252017-02-20 22:56:03 +01002488 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002489
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002490 ctx->frag_ctx.spoe_appctx = NULL;
2491 ctx->frag_ctx.curmsg = NULL;
2492 ctx->frag_ctx.curarg = NULL;
2493 ctx->frag_ctx.curoff = 0;
2494 ctx->frag_ctx.flags = 0;
2495
Christopher Fauleta1cda022016-12-21 08:58:06 +01002496 if (!LIST_ISEMPTY(&ctx->list)) {
2497 LIST_DEL(&ctx->list);
2498 LIST_INIT(&ctx->list);
2499 }
2500}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002501
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002502static void
2503spoe_handle_processing_error(struct stream *s, struct spoe_agent *agent,
2504 struct spoe_context *ctx, int dir)
2505{
2506 if (agent->eps_max > 0)
2507 update_freq_ctr(&agent->err_per_sec, 1);
2508
2509 if (agent->var_on_error) {
2510 struct sample smp;
2511
2512 memset(&smp, 0, sizeof(smp));
2513 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2514 smp.data.u.sint = ctx->status_code;
2515 smp.data.type = SMP_T_BOOL;
2516
2517 spoe_set_var(ctx, "txn", agent->var_on_error,
2518 strlen(agent->var_on_error), &smp);
2519 }
2520 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2521 " - failed to process messages: code=%u\n",
2522 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2523 __FUNCTION__, s, ctx->status_code);
2524 send_log(ctx->strm->be, LOG_WARNING,
2525 "SPOE: [%s] failed to process messages: code=%u\n",
2526 agent->id, ctx->status_code);
2527
2528 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2529 ? SPOE_CTX_ST_READY
2530 : SPOE_CTX_ST_NONE);
2531}
2532
Christopher Faulet58d03682017-09-21 16:57:24 +02002533/* Process a list of SPOE messages. First, this functions will process messages
2534 * and send them to an agent in a NOTIFY frame. Then, it will wait a ACK frame
2535 * to process corresponding actions. During all the processing, it returns 0
2536 * and it returns 1 when the processing is finished. If an error occurred, -1
2537 * is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002538static int
Christopher Faulet58d03682017-09-21 16:57:24 +02002539spoe_process_messages(struct stream *s, struct spoe_context *ctx,
2540 struct list *messages, int dir, int type)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002541{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002542 struct spoe_config *conf = FLT_CONF(ctx->filter);
2543 struct spoe_agent *agent = conf->agent;
Christopher Faulet58d03682017-09-21 16:57:24 +02002544 int ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002545
2546 if (ctx->state == SPOE_CTX_ST_ERROR)
2547 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002548
2549 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2550 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet58d03682017-09-21 16:57:24 +02002551 " - failed to process messages: timeout\n",
Christopher Fauletf7a30922016-11-10 15:04:51 +01002552 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet58d03682017-09-21 16:57:24 +02002553 agent->id, __FUNCTION__, s);
Christopher Fauletb067b062017-01-04 16:39:11 +01002554 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002555 goto error;
2556 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002557
2558 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002559 if (agent->eps_max > 0) {
2560 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
2561 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet58d03682017-09-21 16:57:24 +02002562 " - skip processing of messages: max EPS reached\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002563 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet58d03682017-09-21 16:57:24 +02002564 agent->id, __FUNCTION__, s);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002565 goto skip;
2566 }
2567 }
2568
Christopher Fauletf7a30922016-11-10 15:04:51 +01002569 if (!tick_isset(ctx->process_exp)) {
2570 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2571 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2572 ctx->process_exp);
2573 }
Christopher Faulet58d03682017-09-21 16:57:24 +02002574 ret = spoe_start_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002575 if (!ret)
2576 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002577
Christopher Faulet8ef75252017-02-20 22:56:03 +01002578 if (spoe_queue_context(ctx) < 0)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002579 goto error;
2580
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002581 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002582 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002583 }
2584
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002585 if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002586 if (!spoe_acquire_buffer(&ctx->buffer, &ctx->buffer_wait))
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002587 goto out;
Christopher Faulet58d03682017-09-21 16:57:24 +02002588 ret = spoe_encode_messages(s, ctx, messages, dir, type);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002589 if (ret < 0)
2590 goto error;
Christopher Faulet57583e42017-09-04 15:41:09 +02002591 if (!ret)
2592 goto skip;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002593 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2594 }
2595
2596 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
2597 if (ctx->frag_ctx.spoe_appctx)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002598 spoe_wakeup_appctx(ctx->frag_ctx.spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002599 ret = 0;
2600 goto out;
2601 }
2602
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002603 if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
2604 ret = 0;
2605 goto out;
2606 }
2607
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002608 if (ctx->state == SPOE_CTX_ST_DONE) {
Christopher Faulet58d03682017-09-21 16:57:24 +02002609 spoe_process_actions(s, ctx, dir);
Christopher Faulet8ef75252017-02-20 22:56:03 +01002610 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002611 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002612 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002613 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002614 }
2615
2616 out:
2617 return ret;
2618
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002619 error:
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002620 spoe_handle_processing_error(s, agent, ctx, dir);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002621 ret = 1;
2622 goto end;
2623
2624 skip:
2625 ctx->state = SPOE_CTX_ST_READY;
2626 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002627
Christopher Fauleta1cda022016-12-21 08:58:06 +01002628 end:
Christopher Faulet58d03682017-09-21 16:57:24 +02002629 spoe_stop_processing(ctx);
2630 return ret;
2631}
2632
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002633/* Process a SPOE group, ie the list of messages attached to the group <grp>.
2634 * See spoe_process_message for details. */
2635static int
2636spoe_process_group(struct stream *s, struct spoe_context *ctx,
2637 struct spoe_group *group, int dir)
2638{
2639 int ret;
2640
2641 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2642 " - ctx-state=%s - Process messages for group=%s\n",
2643 (int)now.tv_sec, (int)now.tv_usec,
2644 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2645 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2646 group->id);
2647
2648 if (LIST_ISEMPTY(&group->messages))
2649 return 1;
2650
2651 ret = spoe_process_messages(s, ctx, &group->messages, dir, SPOE_MSGS_BY_GROUP);
2652 return ret;
2653}
2654
Christopher Faulet58d03682017-09-21 16:57:24 +02002655/* Process a SPOE event, ie the list of messages attached to the event <ev>.
2656 * See spoe_process_message for details. */
2657static int
2658spoe_process_event(struct stream *s, struct spoe_context *ctx,
2659 enum spoe_event ev)
2660{
2661 int dir, ret;
2662
2663 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002664 " - ctx-state=%s - Process messages for event=%s\n",
Christopher Faulet58d03682017-09-21 16:57:24 +02002665 (int)now.tv_sec, (int)now.tv_usec,
2666 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2667 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2668 spoe_event_str[ev]);
2669
2670 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2671
2672 if (LIST_ISEMPTY(&(ctx->events[ev])))
2673 return 1;
2674
2675 ret = spoe_process_messages(s, ctx, &(ctx->events[ev]), dir, SPOE_MSGS_BY_EVENT);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002676 return ret;
2677}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002678
2679/***************************************************************************
2680 * Functions that create/destroy SPOE contexts
2681 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002682static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002683spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002684{
Christopher Faulet4596fb72017-01-11 14:05:19 +01002685 if (*buf != &buf_empty)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002686 return 1;
2687
Christopher Faulet4596fb72017-01-11 14:05:19 +01002688 if (!LIST_ISEMPTY(&buffer_wait->list)) {
Emeric Bruna1dd2432017-06-21 15:42:52 +02002689 SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Faulet4596fb72017-01-11 14:05:19 +01002690 LIST_DEL(&buffer_wait->list);
2691 LIST_INIT(&buffer_wait->list);
Emeric Bruna1dd2432017-06-21 15:42:52 +02002692 SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002693 }
2694
Christopher Faulet4596fb72017-01-11 14:05:19 +01002695 if (b_alloc_margin(buf, global.tune.reserved_bufs))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002696 return 1;
2697
Emeric Bruna1dd2432017-06-21 15:42:52 +02002698 SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Faulet4596fb72017-01-11 14:05:19 +01002699 LIST_ADDQ(&buffer_wq, &buffer_wait->list);
Emeric Bruna1dd2432017-06-21 15:42:52 +02002700 SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002701 return 0;
2702}
2703
2704static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002705spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002706{
Christopher Faulet4596fb72017-01-11 14:05:19 +01002707 if (!LIST_ISEMPTY(&buffer_wait->list)) {
Emeric Bruna1dd2432017-06-21 15:42:52 +02002708 SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Faulet4596fb72017-01-11 14:05:19 +01002709 LIST_DEL(&buffer_wait->list);
2710 LIST_INIT(&buffer_wait->list);
Emeric Bruna1dd2432017-06-21 15:42:52 +02002711 SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002712 }
2713
2714 /* Release the buffer if needed */
Christopher Faulet4596fb72017-01-11 14:05:19 +01002715 if (*buf != &buf_empty) {
2716 b_free(buf);
2717 offer_buffers(buffer_wait->target,
2718 tasks_run_queue + applets_active_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002719 }
2720}
2721
Christopher Faulet4596fb72017-01-11 14:05:19 +01002722static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002723spoe_wakeup_context(struct spoe_context *ctx)
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002724{
2725 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2726 return 1;
2727}
2728
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002729static struct spoe_context *
Christopher Faulet8ef75252017-02-20 22:56:03 +01002730spoe_create_context(struct filter *filter)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002731{
2732 struct spoe_config *conf = FLT_CONF(filter);
2733 struct spoe_context *ctx;
2734
2735 ctx = pool_alloc_dirty(pool2_spoe_ctx);
2736 if (ctx == NULL) {
2737 return NULL;
2738 }
2739 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01002740 ctx->filter = filter;
2741 ctx->state = SPOE_CTX_ST_NONE;
2742 ctx->status_code = SPOE_CTX_ERR_NONE;
2743 ctx->flags = 0;
Christopher Faulet11610f32017-09-21 10:23:10 +02002744 ctx->events = conf->agent->events;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02002745 ctx->groups = &conf->agent->groups;
Christopher Fauletb067b062017-01-04 16:39:11 +01002746 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002747 LIST_INIT(&ctx->buffer_wait.list);
2748 ctx->buffer_wait.target = ctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002749 ctx->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002750 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002751
Christopher Fauletf7a30922016-11-10 15:04:51 +01002752 ctx->stream_id = 0;
2753 ctx->frame_id = 1;
2754 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002755
2756 return ctx;
2757}
2758
2759static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002760spoe_destroy_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002761{
2762 if (!ctx)
2763 return;
2764
Christopher Faulet58d03682017-09-21 16:57:24 +02002765 spoe_stop_processing(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002766 pool_free2(pool2_spoe_ctx, ctx);
2767}
2768
2769static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002770spoe_reset_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002771{
2772 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01002773 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002774}
2775
2776
2777/***************************************************************************
2778 * Hooks that manage the filter lifecycle (init/check/deinit)
2779 **************************************************************************/
2780/* Signal handler: Do a soft stop, wakeup SPOE applet */
2781static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002782spoe_sig_stop(struct sig_handler *sh)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002783{
2784 struct proxy *p;
2785
2786 p = proxy;
2787 while (p) {
2788 struct flt_conf *fconf;
2789
2790 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01002791 struct spoe_config *conf;
2792 struct spoe_agent *agent;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002793 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002794
Christopher Faulet3b386a32017-02-23 10:17:15 +01002795 if (fconf->id != spoe_filter_id)
2796 continue;
2797
2798 conf = fconf->conf;
2799 agent = conf->agent;
2800
Christopher Faulet42bfa462017-01-04 14:14:19 +01002801 list_for_each_entry(spoe_appctx, &agent->applets, list) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002802 spoe_wakeup_appctx(spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002803 }
2804 }
2805 p = p->next;
2806 }
2807}
2808
2809
2810/* Initialize the SPOE filter. Returns -1 on error, else 0. */
2811static int
2812spoe_init(struct proxy *px, struct flt_conf *fconf)
2813{
2814 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002815
2816 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
2817 init_new_proxy(&conf->agent_fe);
2818 conf->agent_fe.parent = conf->agent;
2819 conf->agent_fe.last_change = now.tv_sec;
2820 conf->agent_fe.id = conf->agent->id;
2821 conf->agent_fe.cap = PR_CAP_FE;
2822 conf->agent_fe.mode = PR_MODE_TCP;
2823 conf->agent_fe.maxconn = 0;
2824 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
2825 conf->agent_fe.conn_retries = CONN_RETRIES;
2826 conf->agent_fe.accept = frontend_accept;
2827 conf->agent_fe.srv = NULL;
2828 conf->agent_fe.timeout.client = TICK_ETERNITY;
2829 conf->agent_fe.default_target = &spoe_applet.obj_type;
2830 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
2831
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002832 if (!sighandler_registered) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002833 signal_register_fct(0, spoe_sig_stop, 0);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002834 sighandler_registered = 1;
2835 }
2836
2837 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002838}
2839
2840/* Free ressources allocated by the SPOE filter. */
2841static void
2842spoe_deinit(struct proxy *px, struct flt_conf *fconf)
2843{
2844 struct spoe_config *conf = fconf->conf;
2845
2846 if (conf) {
2847 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002848
Christopher Faulet8ef75252017-02-20 22:56:03 +01002849 spoe_release_agent(agent);
Christopher Faulet7ee86672017-09-19 11:08:28 +02002850 free(conf->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002851 free(conf);
2852 }
2853 fconf->conf = NULL;
2854}
2855
2856/* Check configuration of a SPOE filter for a specified proxy.
2857 * Return 1 on error, else 0. */
2858static int
2859spoe_check(struct proxy *px, struct flt_conf *fconf)
2860{
Christopher Faulet7ee86672017-09-19 11:08:28 +02002861 struct flt_conf *f;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002862 struct spoe_config *conf = fconf->conf;
2863 struct proxy *target;
2864
Christopher Faulet7ee86672017-09-19 11:08:28 +02002865 /* Check all SPOE filters for proxy <px> to be sure all SPOE agent names
2866 * are uniq */
2867 list_for_each_entry(f, &px->filter_configs, list) {
2868 struct spoe_config *c = f->conf;
2869
2870 /* This is not an SPOE filter */
2871 if (f->id != spoe_filter_id)
2872 continue;
2873 /* This is the current SPOE filter */
2874 if (f == fconf)
2875 continue;
2876
2877 /* Check engine Id. It should be uniq */
2878 if (!strcmp(conf->id, c->id)) {
2879 Alert("Proxy %s : duplicated name for SPOE engine '%s'.\n",
2880 px->id, conf->id);
2881 return 1;
2882 }
2883 }
2884
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002885 target = proxy_be_by_name(conf->agent->b.name);
2886 if (target == NULL) {
2887 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
2888 " declared at %s:%d.\n",
2889 px->id, conf->agent->b.name, conf->agent->id,
2890 conf->agent->conf.file, conf->agent->conf.line);
2891 return 1;
2892 }
2893 if (target->mode != PR_MODE_TCP) {
2894 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
2895 " at %s:%d does not support HTTP mode.\n",
2896 px->id, target->id, conf->agent->id,
2897 conf->agent->conf.file, conf->agent->conf.line);
2898 return 1;
2899 }
2900
2901 free(conf->agent->b.name);
2902 conf->agent->b.name = NULL;
2903 conf->agent->b.be = target;
2904 return 0;
2905}
2906
2907/**************************************************************************
2908 * Hooks attached to a stream
2909 *************************************************************************/
2910/* Called when a filter instance is created and attach to a stream. It creates
2911 * the context that will be used to process this stream. */
2912static int
2913spoe_start(struct stream *s, struct filter *filter)
2914{
Christopher Faulet72bcc472017-01-04 16:39:41 +01002915 struct spoe_config *conf = FLT_CONF(filter);
2916 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002917 struct spoe_context *ctx;
2918
2919 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01002920 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002921 __FUNCTION__, s);
2922
Christopher Faulet8ef75252017-02-20 22:56:03 +01002923 ctx = spoe_create_context(filter);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002924 if (ctx == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01002925 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2926 " - failed to create SPOE context\n",
2927 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletccbc3fd2017-09-15 11:51:18 +02002928 __FUNCTION__, s);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002929 send_log(s->be, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01002930 "SPOE: [%s] failed to create SPOE context\n",
2931 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002932 return 0;
2933 }
2934
2935 ctx->strm = s;
2936 ctx->state = SPOE_CTX_ST_READY;
2937 filter->ctx = ctx;
2938
Christopher Faulet11610f32017-09-21 10:23:10 +02002939 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_FE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002940 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
2941
Christopher Faulet11610f32017-09-21 10:23:10 +02002942 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_BE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002943 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
2944
Christopher Faulet11610f32017-09-21 10:23:10 +02002945 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_RSP]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002946 filter->pre_analyzers |= AN_RES_INSPECT;
2947
Christopher Faulet11610f32017-09-21 10:23:10 +02002948 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_FE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002949 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
2950
Christopher Faulet11610f32017-09-21 10:23:10 +02002951 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_BE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002952 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
2953
Christopher Faulet11610f32017-09-21 10:23:10 +02002954 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_RSP]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002955 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
2956
2957 return 1;
2958}
2959
2960/* Called when a filter instance is detached from a stream. It release the
2961 * attached SPOE context. */
2962static void
2963spoe_stop(struct stream *s, struct filter *filter)
2964{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002965 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
2966 (int)now.tv_sec, (int)now.tv_usec,
2967 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2968 __FUNCTION__, s);
Christopher Faulet8ef75252017-02-20 22:56:03 +01002969 spoe_destroy_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002970}
2971
Christopher Fauletf7a30922016-11-10 15:04:51 +01002972
2973/*
2974 * Called when the stream is woken up because of expired timer.
2975 */
2976static void
2977spoe_check_timeouts(struct stream *s, struct filter *filter)
2978{
2979 struct spoe_context *ctx = filter->ctx;
2980
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002981 if (tick_is_expired(ctx->process_exp, now_ms)) {
2982 s->pending_events |= TASK_WOKEN_MSG;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002983 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002984 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01002985}
2986
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002987/* Called when we are ready to filter data on a channel */
2988static int
2989spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
2990{
2991 struct spoe_context *ctx = filter->ctx;
2992 int ret = 1;
2993
2994 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2995 " - ctx-flags=0x%08x\n",
2996 (int)now.tv_sec, (int)now.tv_usec,
2997 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2998 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
2999
Christopher Fauletb067b062017-01-04 16:39:11 +01003000 if (ctx->state == SPOE_CTX_ST_NONE)
3001 goto out;
3002
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003003 if (!(chn->flags & CF_ISRESP)) {
3004 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
3005 chn->analysers |= AN_REQ_INSPECT_FE;
3006 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
3007 chn->analysers |= AN_REQ_INSPECT_BE;
3008
3009 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
3010 goto out;
3011
3012 ctx->stream_id = s->uniq_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003013 ret = spoe_process_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
Christopher Fauletb067b062017-01-04 16:39:11 +01003014 if (!ret)
3015 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003016 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
3017 }
3018 else {
3019 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
3020 chn->analysers |= AN_RES_INSPECT;
3021
3022 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
3023 goto out;
3024
Christopher Faulet8ef75252017-02-20 22:56:03 +01003025 ret = spoe_process_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003026 if (!ret) {
3027 channel_dont_read(chn);
3028 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01003029 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003030 }
Christopher Fauletb067b062017-01-04 16:39:11 +01003031 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003032 }
3033
3034 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003035 return ret;
3036}
3037
3038/* Called before a processing happens on a given channel */
3039static int
3040spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
3041 struct channel *chn, unsigned an_bit)
3042{
3043 struct spoe_context *ctx = filter->ctx;
3044 int ret = 1;
3045
3046 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3047 " - ctx-flags=0x%08x - ana=0x%08x\n",
3048 (int)now.tv_sec, (int)now.tv_usec,
3049 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3050 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
3051 ctx->flags, an_bit);
3052
Christopher Fauletb067b062017-01-04 16:39:11 +01003053 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003054 goto out;
3055
3056 switch (an_bit) {
3057 case AN_REQ_INSPECT_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003058 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003059 break;
3060 case AN_REQ_INSPECT_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003061 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003062 break;
3063 case AN_RES_INSPECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003064 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003065 break;
3066 case AN_REQ_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003067 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003068 break;
3069 case AN_REQ_HTTP_PROCESS_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003070 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003071 break;
3072 case AN_RES_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003073 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003074 break;
3075 }
3076
3077 out:
3078 if (!ret) {
3079 channel_dont_read(chn);
3080 channel_dont_close(chn);
3081 }
3082 return ret;
3083}
3084
3085/* Called when the filtering on the channel ends. */
3086static int
3087spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3088{
3089 struct spoe_context *ctx = filter->ctx;
3090
3091 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3092 " - ctx-flags=0x%08x\n",
3093 (int)now.tv_sec, (int)now.tv_usec,
3094 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3095 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3096
3097 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003098 spoe_reset_context(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003099 }
3100
3101 return 1;
3102}
3103
3104/********************************************************************
3105 * Functions that manage the filter initialization
3106 ********************************************************************/
3107struct flt_ops spoe_ops = {
3108 /* Manage SPOE filter, called for each filter declaration */
3109 .init = spoe_init,
3110 .deinit = spoe_deinit,
3111 .check = spoe_check,
3112
3113 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01003114 .attach = spoe_start,
3115 .detach = spoe_stop,
3116 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003117
3118 /* Handle channels activity */
3119 .channel_start_analyze = spoe_start_analyze,
3120 .channel_pre_analyze = spoe_chn_pre_analyze,
3121 .channel_end_analyze = spoe_end_analyze,
3122};
3123
3124
3125static int
3126cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
3127{
3128 const char *err;
3129 int i, err_code = 0;
3130
3131 if ((cfg_scope == NULL && curengine != NULL) ||
3132 (cfg_scope != NULL && curengine == NULL) ||
Christopher Faulete1405e52017-09-19 10:35:35 +02003133 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003134 goto out;
3135
3136 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
3137 if (!*args[1]) {
3138 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
3139 file, linenum);
3140 err_code |= ERR_ALERT | ERR_ABORT;
3141 goto out;
3142 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003143 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3144 err_code |= ERR_ABORT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003145 goto out;
3146 }
3147
3148 err = invalid_char(args[1]);
3149 if (err) {
3150 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3151 file, linenum, *err, args[0], args[1]);
3152 err_code |= ERR_ALERT | ERR_ABORT;
3153 goto out;
3154 }
3155
3156 if (curagent != NULL) {
3157 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
3158 file, linenum);
3159 err_code |= ERR_ALERT | ERR_ABORT;
3160 goto out;
3161 }
3162 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
3163 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3164 err_code |= ERR_ALERT | ERR_ABORT;
3165 goto out;
3166 }
3167
3168 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003169
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003170 curagent->conf.file = strdup(file);
3171 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003172
3173 curagent->timeout.hello = TICK_ETERNITY;
3174 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003175 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003176
3177 curagent->engine_id = NULL;
3178 curagent->var_pfx = NULL;
3179 curagent->var_on_error = NULL;
Christopher Fauletcecd8522017-02-24 22:11:21 +01003180 curagent->flags = (SPOE_FL_PIPELINING | SPOE_FL_ASYNC | SPOE_FL_SND_FRAGMENTATION);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003181 curagent->cps_max = 0;
3182 curagent->eps_max = 0;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003183 curagent->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003184 curagent->min_applets = 0;
3185 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003186
3187 for (i = 0; i < SPOE_EV_EVENTS; ++i)
Christopher Faulet11610f32017-09-21 10:23:10 +02003188 LIST_INIT(&curagent->events[i]);
3189 LIST_INIT(&curagent->groups);
3190 LIST_INIT(&curagent->messages);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003191
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003192 curagent->frame_size = curagent->max_frame_size;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003193 curagent->applets_act = 0;
3194 curagent->applets_idle = 0;
3195 curagent->sending_rate = 0;
3196
3197 LIST_INIT(&curagent->applets);
3198 LIST_INIT(&curagent->sending_queue);
3199 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003200 }
3201 else if (!strcmp(args[0], "use-backend")) {
3202 if (!*args[1]) {
3203 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3204 file, linenum, args[0]);
3205 err_code |= ERR_ALERT | ERR_FATAL;
3206 goto out;
3207 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003208 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003209 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003210 free(curagent->b.name);
3211 curagent->b.name = strdup(args[1]);
3212 }
3213 else if (!strcmp(args[0], "messages")) {
3214 int cur_arg = 1;
3215 while (*args[cur_arg]) {
Christopher Faulet11610f32017-09-21 10:23:10 +02003216 struct spoe_placeholder *ph = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003217
Christopher Faulet11610f32017-09-21 10:23:10 +02003218 list_for_each_entry(ph, &curmphs, list) {
3219 if (!strcmp(ph->id, args[cur_arg])) {
3220 Alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003221 file, linenum, args[cur_arg]);
3222 err_code |= ERR_ALERT | ERR_FATAL;
3223 goto out;
3224 }
3225 }
3226
Christopher Faulet11610f32017-09-21 10:23:10 +02003227 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003228 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3229 err_code |= ERR_ALERT | ERR_ABORT;
3230 goto out;
3231 }
Christopher Faulet11610f32017-09-21 10:23:10 +02003232 ph->id = strdup(args[cur_arg]);
3233 LIST_ADDQ(&curmphs, &ph->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003234 cur_arg++;
3235 }
3236 }
Christopher Faulet11610f32017-09-21 10:23:10 +02003237 else if (!strcmp(args[0], "groups")) {
3238 int cur_arg = 1;
3239 while (*args[cur_arg]) {
3240 struct spoe_placeholder *ph = NULL;
3241
3242 list_for_each_entry(ph, &curgphs, list) {
3243 if (!strcmp(ph->id, args[cur_arg])) {
3244 Alert("parsing [%s:%d]: spoe-group '%s' already used.\n",
3245 file, linenum, args[cur_arg]);
3246 err_code |= ERR_ALERT | ERR_FATAL;
3247 goto out;
3248 }
3249 }
3250
3251 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
3252 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3253 err_code |= ERR_ALERT | ERR_ABORT;
3254 goto out;
3255 }
3256 ph->id = strdup(args[cur_arg]);
3257 LIST_ADDQ(&curgphs, &ph->list);
3258 cur_arg++;
3259 }
3260 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003261 else if (!strcmp(args[0], "timeout")) {
3262 unsigned int *tv = NULL;
3263 const char *res;
3264 unsigned timeout;
3265
3266 if (!*args[1]) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003267 Alert("parsing [%s:%d] : 'timeout' expects 'hello', 'idle' and 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003268 file, linenum);
3269 err_code |= ERR_ALERT | ERR_FATAL;
3270 goto out;
3271 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003272 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3273 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003274 if (!strcmp(args[1], "hello"))
3275 tv = &curagent->timeout.hello;
3276 else if (!strcmp(args[1], "idle"))
3277 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003278 else if (!strcmp(args[1], "processing"))
3279 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003280 else {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003281 Alert("parsing [%s:%d] : 'timeout' supports 'hello', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003282 file, linenum, args[1]);
3283 err_code |= ERR_ALERT | ERR_FATAL;
3284 goto out;
3285 }
3286 if (!*args[2]) {
3287 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3288 file, linenum, args[1]);
3289 err_code |= ERR_ALERT | ERR_FATAL;
3290 goto out;
3291 }
3292 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3293 if (res) {
3294 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3295 file, linenum, *res, args[1]);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003296 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003297 goto out;
3298 }
3299 *tv = MS_TO_TICKS(timeout);
3300 }
3301 else if (!strcmp(args[0], "option")) {
3302 if (!*args[1]) {
3303 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3304 file, linenum, args[0]);
3305 err_code |= ERR_ALERT | ERR_FATAL;
3306 goto out;
3307 }
Christopher Faulet6a2940c2017-02-23 15:06:26 +01003308
Christopher Faulet305c6072017-02-23 16:17:53 +01003309 if (!strcmp(args[1], "pipelining")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003310 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet305c6072017-02-23 16:17:53 +01003311 goto out;
Christopher Faulet305c6072017-02-23 16:17:53 +01003312 if (kwm == 1)
3313 curagent->flags &= ~SPOE_FL_PIPELINING;
3314 else
3315 curagent->flags |= SPOE_FL_PIPELINING;
3316 goto out;
3317 }
3318 else if (!strcmp(args[1], "async")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003319 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet305c6072017-02-23 16:17:53 +01003320 goto out;
Christopher Faulet305c6072017-02-23 16:17:53 +01003321 if (kwm == 1)
3322 curagent->flags &= ~SPOE_FL_ASYNC;
3323 else
3324 curagent->flags |= SPOE_FL_ASYNC;
3325 goto out;
3326 }
Christopher Fauletcecd8522017-02-24 22:11:21 +01003327 else if (!strcmp(args[1], "send-frag-payload")) {
3328 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3329 goto out;
3330 if (kwm == 1)
3331 curagent->flags &= ~SPOE_FL_SND_FRAGMENTATION;
3332 else
3333 curagent->flags |= SPOE_FL_SND_FRAGMENTATION;
3334 goto out;
3335 }
Christopher Faulet305c6072017-02-23 16:17:53 +01003336
Christopher Faulet6a2940c2017-02-23 15:06:26 +01003337 /* Following options does not support negation */
3338 if (kwm == 1) {
3339 Alert("parsing [%s:%d]: negation is not supported for option '%s'.\n",
3340 file, linenum, args[1]);
3341 err_code |= ERR_ALERT | ERR_FATAL;
3342 goto out;
3343 }
3344
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003345 if (!strcmp(args[1], "var-prefix")) {
3346 char *tmp;
3347
3348 if (!*args[2]) {
3349 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3350 file, linenum, args[0],
3351 args[1]);
3352 err_code |= ERR_ALERT | ERR_FATAL;
3353 goto out;
3354 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003355 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3356 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003357 tmp = args[2];
3358 while (*tmp) {
3359 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3360 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3361 file, linenum, args[0], args[1]);
3362 err_code |= ERR_ALERT | ERR_FATAL;
3363 goto out;
3364 }
3365 tmp++;
3366 }
3367 curagent->var_pfx = strdup(args[2]);
3368 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003369 else if (!strcmp(args[1], "continue-on-error")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003370 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003371 goto out;
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003372 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3373 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003374 else if (!strcmp(args[1], "set-on-error")) {
3375 char *tmp;
3376
3377 if (!*args[2]) {
3378 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3379 file, linenum, args[0],
3380 args[1]);
3381 err_code |= ERR_ALERT | ERR_FATAL;
3382 goto out;
3383 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003384 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3385 goto out;
Christopher Faulet985532d2016-11-16 15:36:19 +01003386 tmp = args[2];
3387 while (*tmp) {
3388 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3389 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3390 file, linenum, args[0], args[1]);
3391 err_code |= ERR_ALERT | ERR_FATAL;
3392 goto out;
3393 }
3394 tmp++;
3395 }
3396 curagent->var_on_error = strdup(args[2]);
3397 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003398 else {
3399 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3400 file, linenum, args[1]);
3401 err_code |= ERR_ALERT | ERR_FATAL;
3402 goto out;
3403 }
Christopher Faulet48026722016-11-16 15:01:12 +01003404 }
3405 else if (!strcmp(args[0], "maxconnrate")) {
3406 if (!*args[1]) {
3407 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3408 file, linenum, args[0]);
3409 err_code |= ERR_ALERT | ERR_FATAL;
3410 goto out;
3411 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003412 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet48026722016-11-16 15:01:12 +01003413 goto out;
Christopher Faulet48026722016-11-16 15:01:12 +01003414 curagent->cps_max = atol(args[1]);
3415 }
3416 else if (!strcmp(args[0], "maxerrrate")) {
3417 if (!*args[1]) {
3418 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3419 file, linenum, args[0]);
3420 err_code |= ERR_ALERT | ERR_FATAL;
3421 goto out;
3422 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003423 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet48026722016-11-16 15:01:12 +01003424 goto out;
Christopher Faulet48026722016-11-16 15:01:12 +01003425 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003426 }
Christopher Faulet2eca6b52017-02-27 09:40:34 +01003427 else if (!strcmp(args[0], "max-frame-size")) {
3428 if (!*args[1]) {
3429 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3430 file, linenum, args[0]);
3431 err_code |= ERR_ALERT | ERR_FATAL;
3432 goto out;
3433 }
3434 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3435 goto out;
3436 curagent->max_frame_size = atol(args[1]);
3437 if (curagent->max_frame_size < MIN_FRAME_SIZE ||
3438 curagent->max_frame_size > MAX_FRAME_SIZE) {
3439 Alert("parsing [%s:%d] : '%s' expects a positive integer argument in the range [%d, %d].\n",
3440 file, linenum, args[0], MIN_FRAME_SIZE, MAX_FRAME_SIZE);
3441 err_code |= ERR_ALERT | ERR_FATAL;
3442 goto out;
3443 }
3444 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003445 else if (*args[0]) {
3446 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3447 file, linenum, args[0]);
3448 err_code |= ERR_ALERT | ERR_FATAL;
3449 goto out;
3450 }
3451 out:
3452 return err_code;
3453}
Christopher Faulet11610f32017-09-21 10:23:10 +02003454static int
3455cfg_parse_spoe_group(const char *file, int linenum, char **args, int kwm)
3456{
3457 struct spoe_group *grp;
3458 const char *err;
3459 int err_code = 0;
3460
3461 if ((cfg_scope == NULL && curengine != NULL) ||
3462 (cfg_scope != NULL && curengine == NULL) ||
3463 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
3464 goto out;
3465
3466 if (!strcmp(args[0], "spoe-group")) { /* new spoe-group section */
3467 if (!*args[1]) {
3468 Alert("parsing [%s:%d] : missing name for spoe-group section.\n",
3469 file, linenum);
3470 err_code |= ERR_ALERT | ERR_ABORT;
3471 goto out;
3472 }
3473 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3474 err_code |= ERR_ABORT;
3475 goto out;
3476 }
3477
3478 err = invalid_char(args[1]);
3479 if (err) {
3480 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3481 file, linenum, *err, args[0], args[1]);
3482 err_code |= ERR_ALERT | ERR_ABORT;
3483 goto out;
3484 }
3485
3486 list_for_each_entry(grp, &curgrps, list) {
3487 if (!strcmp(grp->id, args[1])) {
3488 Alert("parsing [%s:%d]: spoe-group section '%s' has the same"
3489 " name as another one declared at %s:%d.\n",
3490 file, linenum, args[1], grp->conf.file, grp->conf.line);
3491 err_code |= ERR_ALERT | ERR_FATAL;
3492 goto out;
3493 }
3494 }
3495
3496 if ((curgrp = calloc(1, sizeof(*curgrp))) == NULL) {
3497 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3498 err_code |= ERR_ALERT | ERR_ABORT;
3499 goto out;
3500 }
3501
3502 curgrp->id = strdup(args[1]);
3503 curgrp->conf.file = strdup(file);
3504 curgrp->conf.line = linenum;
3505 LIST_INIT(&curgrp->phs);
3506 LIST_INIT(&curgrp->messages);
3507 LIST_ADDQ(&curgrps, &curgrp->list);
3508 }
3509 else if (!strcmp(args[0], "messages")) {
3510 int cur_arg = 1;
3511 while (*args[cur_arg]) {
3512 struct spoe_placeholder *ph = NULL;
3513
3514 list_for_each_entry(ph, &curgrp->phs, list) {
3515 if (!strcmp(ph->id, args[cur_arg])) {
3516 Alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
3517 file, linenum, args[cur_arg]);
3518 err_code |= ERR_ALERT | ERR_FATAL;
3519 goto out;
3520 }
3521 }
3522
3523 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
3524 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3525 err_code |= ERR_ALERT | ERR_ABORT;
3526 goto out;
3527 }
3528 ph->id = strdup(args[cur_arg]);
3529 LIST_ADDQ(&curgrp->phs, &ph->list);
3530 cur_arg++;
3531 }
3532 }
3533 else if (*args[0]) {
3534 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-group section.\n",
3535 file, linenum, args[0]);
3536 err_code |= ERR_ALERT | ERR_FATAL;
3537 goto out;
3538 }
3539 out:
3540 return err_code;
3541}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003542
3543static int
3544cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3545{
3546 struct spoe_message *msg;
3547 struct spoe_arg *arg;
3548 const char *err;
3549 char *errmsg = NULL;
3550 int err_code = 0;
3551
3552 if ((cfg_scope == NULL && curengine != NULL) ||
3553 (cfg_scope != NULL && curengine == NULL) ||
Christopher Faulete1405e52017-09-19 10:35:35 +02003554 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003555 goto out;
3556
3557 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3558 if (!*args[1]) {
3559 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3560 file, linenum);
3561 err_code |= ERR_ALERT | ERR_ABORT;
3562 goto out;
3563 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003564 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3565 err_code |= ERR_ABORT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003566 goto out;
3567 }
3568
3569 err = invalid_char(args[1]);
3570 if (err) {
3571 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3572 file, linenum, *err, args[0], args[1]);
3573 err_code |= ERR_ALERT | ERR_ABORT;
3574 goto out;
3575 }
3576
3577 list_for_each_entry(msg, &curmsgs, list) {
3578 if (!strcmp(msg->id, args[1])) {
3579 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3580 " name as another one declared at %s:%d.\n",
3581 file, linenum, args[1], msg->conf.file, msg->conf.line);
3582 err_code |= ERR_ALERT | ERR_FATAL;
3583 goto out;
3584 }
3585 }
3586
3587 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3588 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3589 err_code |= ERR_ALERT | ERR_ABORT;
3590 goto out;
3591 }
3592
3593 curmsg->id = strdup(args[1]);
3594 curmsg->id_len = strlen(curmsg->id);
3595 curmsg->event = SPOE_EV_NONE;
3596 curmsg->conf.file = strdup(file);
3597 curmsg->conf.line = linenum;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003598 curmsg->nargs = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003599 LIST_INIT(&curmsg->args);
Christopher Faulet57583e42017-09-04 15:41:09 +02003600 LIST_INIT(&curmsg->acls);
Christopher Faulet11610f32017-09-21 10:23:10 +02003601 LIST_INIT(&curmsg->by_evt);
3602 LIST_INIT(&curmsg->by_grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003603 LIST_ADDQ(&curmsgs, &curmsg->list);
3604 }
3605 else if (!strcmp(args[0], "args")) {
3606 int cur_arg = 1;
3607
3608 curproxy->conf.args.ctx = ARGC_SPOE;
3609 curproxy->conf.args.file = file;
3610 curproxy->conf.args.line = linenum;
3611 while (*args[cur_arg]) {
3612 char *delim = strchr(args[cur_arg], '=');
3613 int idx = 0;
3614
3615 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3616 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3617 err_code |= ERR_ALERT | ERR_ABORT;
3618 goto out;
3619 }
3620
3621 if (!delim) {
3622 arg->name = NULL;
3623 arg->name_len = 0;
3624 delim = args[cur_arg];
3625 }
3626 else {
3627 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3628 arg->name_len = delim - args[cur_arg];
3629 delim++;
3630 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003631 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3632 &idx, file, linenum, &errmsg,
3633 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003634 if (arg->expr == NULL) {
3635 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3636 err_code |= ERR_ALERT | ERR_FATAL;
3637 free(arg->name);
3638 free(arg);
3639 goto out;
3640 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003641 curmsg->nargs++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003642 LIST_ADDQ(&curmsg->args, &arg->list);
3643 cur_arg++;
3644 }
3645 curproxy->conf.args.file = NULL;
3646 curproxy->conf.args.line = 0;
3647 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003648 else if (!strcmp(args[0], "acl")) {
3649 err = invalid_char(args[1]);
3650 if (err) {
3651 Alert("parsing [%s:%d] : character '%c' is not permitted in acl name '%s'.\n",
3652 file, linenum, *err, args[1]);
3653 err_code |= ERR_ALERT | ERR_FATAL;
3654 goto out;
3655 }
3656 if (parse_acl((const char **)args + 1, &curmsg->acls, &errmsg, &curproxy->conf.args, file, linenum) == NULL) {
3657 Alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
3658 file, linenum, args[1], errmsg);
3659 err_code |= ERR_ALERT | ERR_FATAL;
3660 goto out;
3661 }
3662 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003663 else if (!strcmp(args[0], "event")) {
3664 if (!*args[1]) {
3665 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003666 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003667 goto out;
3668 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003669 /* if (alertif_too_many_args(1, file, linenum, args, &err_code)) */
3670 /* goto out; */
Christopher Fauletecc537a2017-02-23 22:52:39 +01003671
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003672 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3673 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3674 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3675 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3676
3677 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3678 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3679 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3680 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3681 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3682 curmsg->event = SPOE_EV_ON_TCP_RSP;
3683
3684 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3685 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3686 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3687 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3688 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3689 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3690 else {
3691 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3692 file, linenum, args[1]);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003693 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003694 goto out;
3695 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003696
3697 if (strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0) {
3698 struct acl_cond *cond;
3699
3700 cond = build_acl_cond(file, linenum, &curmsg->acls,
3701 curproxy, (const char **)args+2,
3702 &errmsg);
3703 if (cond == NULL) {
3704 Alert("parsing [%s:%d] : error detected while "
3705 "parsing an 'event %s' condition : %s.\n",
3706 file, linenum, args[1], errmsg);
3707 err_code |= ERR_ALERT | ERR_FATAL;
3708 goto out;
3709 }
3710 curmsg->cond = cond;
3711 }
3712 else if (*args[2]) {
3713 Alert("parsing [%s:%d]: 'event %s' expects either 'if' "
3714 "or 'unless' followed by a condition but found '%s'.\n",
3715 file, linenum, args[1], args[2]);
3716 err_code |= ERR_ALERT | ERR_FATAL;
3717 goto out;
3718 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003719 }
3720 else if (!*args[0]) {
3721 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3722 file, linenum, args[0]);
3723 err_code |= ERR_ALERT | ERR_FATAL;
3724 goto out;
3725 }
3726 out:
3727 free(errmsg);
3728 return err_code;
3729}
3730
3731/* Return -1 on error, else 0 */
3732static int
3733parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
3734 struct flt_conf *fconf, char **err, void *private)
3735{
3736 struct list backup_sections;
3737 struct spoe_config *conf;
3738 struct spoe_message *msg, *msgback;
Christopher Faulet11610f32017-09-21 10:23:10 +02003739 struct spoe_group *grp, *grpback;
3740 struct spoe_placeholder *ph, *phback;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003741 char *file = NULL, *engine = NULL;
3742 int ret, pos = *cur_arg + 1;
3743
3744 conf = calloc(1, sizeof(*conf));
3745 if (conf == NULL) {
3746 memprintf(err, "%s: out of memory", args[*cur_arg]);
3747 goto error;
3748 }
3749 conf->proxy = px;
3750
3751 while (*args[pos]) {
3752 if (!strcmp(args[pos], "config")) {
3753 if (!*args[pos+1]) {
3754 memprintf(err, "'%s' : '%s' option without value",
3755 args[*cur_arg], args[pos]);
3756 goto error;
3757 }
3758 file = args[pos+1];
3759 pos += 2;
3760 }
3761 else if (!strcmp(args[pos], "engine")) {
3762 if (!*args[pos+1]) {
3763 memprintf(err, "'%s' : '%s' option without value",
3764 args[*cur_arg], args[pos]);
3765 goto error;
3766 }
3767 engine = args[pos+1];
3768 pos += 2;
3769 }
3770 else {
3771 memprintf(err, "unknown keyword '%s'", args[pos]);
3772 goto error;
3773 }
3774 }
3775 if (file == NULL) {
3776 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
3777 goto error;
3778 }
3779
3780 /* backup sections and register SPOE sections */
3781 LIST_INIT(&backup_sections);
3782 cfg_backup_sections(&backup_sections);
Christopher Faulet11610f32017-09-21 10:23:10 +02003783 cfg_register_section("spoe-agent", cfg_parse_spoe_agent, NULL);
3784 cfg_register_section("spoe-group", cfg_parse_spoe_group, NULL);
William Lallemandd2ff56d2017-10-16 11:06:50 +02003785 cfg_register_section("spoe-message", cfg_parse_spoe_message, NULL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003786
3787 /* Parse SPOE filter configuration file */
3788 curengine = engine;
3789 curproxy = px;
3790 curagent = NULL;
3791 curmsg = NULL;
Christopher Faulet11610f32017-09-21 10:23:10 +02003792 LIST_INIT(&curmsgs);
3793 LIST_INIT(&curgrps);
3794 LIST_INIT(&curmphs);
3795 LIST_INIT(&curgphs);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003796 ret = readcfgfile(file);
3797 curproxy = NULL;
3798
3799 /* unregister SPOE sections and restore previous sections */
3800 cfg_unregister_sections();
3801 cfg_restore_sections(&backup_sections);
3802
3803 if (ret == -1) {
3804 memprintf(err, "Could not open configuration file %s : %s",
3805 file, strerror(errno));
3806 goto error;
3807 }
3808 if (ret & (ERR_ABORT|ERR_FATAL)) {
3809 memprintf(err, "Error(s) found in configuration file %s", file);
3810 goto error;
3811 }
3812
3813 /* Check SPOE agent */
3814 if (curagent == NULL) {
3815 memprintf(err, "No SPOE agent found in file %s", file);
3816 goto error;
3817 }
3818 if (curagent->b.name == NULL) {
3819 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
3820 curagent->id, curagent->conf.file, curagent->conf.line);
3821 goto error;
3822 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003823 if (curagent->timeout.hello == TICK_ETERNITY ||
3824 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01003825 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003826 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
3827 " | While not properly invalid, you will certainly encounter various problems\n"
3828 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01003829 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003830 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3831 }
3832 if (curagent->var_pfx == NULL) {
3833 char *tmp = curagent->id;
3834
3835 while (*tmp) {
3836 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3837 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
3838 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
3839 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
3840 goto error;
3841 }
3842 tmp++;
3843 }
3844 curagent->var_pfx = strdup(curagent->id);
3845 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01003846 if (curagent->engine_id == NULL)
3847 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003848
Christopher Faulet11610f32017-09-21 10:23:10 +02003849 if (LIST_ISEMPTY(&curmphs) && LIST_ISEMPTY(&curgphs)) {
3850 Warning("Proxy '%s': No message/group used by SPOE agent '%s' declared at %s:%d.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003851 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3852 goto finish;
3853 }
3854
Christopher Faulet11610f32017-09-21 10:23:10 +02003855 /* Replace placeholders by the corresponding messages for the SPOE
3856 * agent */
3857 list_for_each_entry(ph, &curmphs, list) {
3858 list_for_each_entry(msg, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01003859 struct spoe_arg *arg;
3860 unsigned int where;
3861
Christopher Faulet11610f32017-09-21 10:23:10 +02003862 if (!strcmp(msg->id, ph->id)) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003863 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
3864 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
3865 msg->event = SPOE_EV_ON_TCP_REQ_FE;
3866 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
3867 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
3868 }
3869 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
3870 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
3871 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
3872 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
3873 px->id, msg->conf.file, msg->conf.line);
Christopher Faulet11610f32017-09-21 10:23:10 +02003874 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003875 }
3876 if (msg->event == SPOE_EV_NONE) {
Christopher Faulet76c09ef2017-09-21 11:03:52 +02003877 Warning("Proxy '%s': Ignore SPOE message '%s' without event at %s:%d.\n",
3878 px->id, msg->id, msg->conf.file, msg->conf.line);
Christopher Faulet11610f32017-09-21 10:23:10 +02003879 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003880 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01003881
3882 where = 0;
3883 switch (msg->event) {
3884 case SPOE_EV_ON_CLIENT_SESS:
3885 where |= SMP_VAL_FE_CON_ACC;
3886 break;
3887
3888 case SPOE_EV_ON_TCP_REQ_FE:
3889 where |= SMP_VAL_FE_REQ_CNT;
3890 break;
3891
3892 case SPOE_EV_ON_HTTP_REQ_FE:
3893 where |= SMP_VAL_FE_HRQ_HDR;
3894 break;
3895
3896 case SPOE_EV_ON_TCP_REQ_BE:
3897 if (px->cap & PR_CAP_FE)
3898 where |= SMP_VAL_FE_REQ_CNT;
3899 if (px->cap & PR_CAP_BE)
3900 where |= SMP_VAL_BE_REQ_CNT;
3901 break;
3902
3903 case SPOE_EV_ON_HTTP_REQ_BE:
3904 if (px->cap & PR_CAP_FE)
3905 where |= SMP_VAL_FE_HRQ_HDR;
3906 if (px->cap & PR_CAP_BE)
3907 where |= SMP_VAL_BE_HRQ_HDR;
3908 break;
3909
3910 case SPOE_EV_ON_SERVER_SESS:
3911 where |= SMP_VAL_BE_SRV_CON;
3912 break;
3913
3914 case SPOE_EV_ON_TCP_RSP:
3915 if (px->cap & PR_CAP_FE)
3916 where |= SMP_VAL_FE_RES_CNT;
3917 if (px->cap & PR_CAP_BE)
3918 where |= SMP_VAL_BE_RES_CNT;
3919 break;
3920
3921 case SPOE_EV_ON_HTTP_RSP:
3922 if (px->cap & PR_CAP_FE)
3923 where |= SMP_VAL_FE_HRS_HDR;
3924 if (px->cap & PR_CAP_BE)
3925 where |= SMP_VAL_BE_HRS_HDR;
3926 break;
3927
3928 default:
3929 break;
3930 }
3931
3932 list_for_each_entry(arg, &msg->args, list) {
3933 if (!(arg->expr->fetch->val & where)) {
Christopher Faulet76c09ef2017-09-21 11:03:52 +02003934 memprintf(err, "Ignore SPOE message '%s' at %s:%d: "
Christopher Fauleta21b0642017-01-09 16:56:23 +01003935 "some args extract information from '%s', "
Christopher Faulet76c09ef2017-09-21 11:03:52 +02003936 "none of which is available here ('%s')",
3937 msg->id, msg->conf.file, msg->conf.line,
Christopher Fauleta21b0642017-01-09 16:56:23 +01003938 sample_ckp_names(arg->expr->fetch->use),
3939 sample_ckp_names(where));
Christopher Faulet76c09ef2017-09-21 11:03:52 +02003940 goto error;
Christopher Fauleta21b0642017-01-09 16:56:23 +01003941 }
3942 }
3943
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003944 msg->agent = curagent;
Christopher Faulet11610f32017-09-21 10:23:10 +02003945 LIST_ADDQ(&curagent->events[msg->event], &msg->by_evt);
3946 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003947 }
3948 }
3949 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
Christopher Faulet11610f32017-09-21 10:23:10 +02003950 curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003951 goto error;
Christopher Faulet11610f32017-09-21 10:23:10 +02003952 next_mph:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003953 continue;
3954 }
3955
Christopher Faulet11610f32017-09-21 10:23:10 +02003956 /* Replace placeholders by the corresponding groups for the SPOE
3957 * agent */
3958 list_for_each_entry(ph, &curgphs, list) {
3959 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
3960 if (!strcmp(grp->id, ph->id)) {
3961 grp->agent = curagent;
3962 LIST_DEL(&grp->list);
3963 LIST_ADDQ(&curagent->groups, &grp->list);
3964 goto next_aph;
3965 }
3966 }
3967 memprintf(err, "SPOE agent '%s' try to use undefined SPOE group '%s' at %s:%d",
3968 curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
3969 goto error;
3970 next_aph:
3971 continue;
3972 }
3973
3974 /* Replace placeholders by the corresponding message for each SPOE
3975 * group of the SPOE agent */
3976 list_for_each_entry(grp, &curagent->groups, list) {
3977 list_for_each_entry_safe(ph, phback, &grp->phs, list) {
3978 list_for_each_entry(msg, &curmsgs, list) {
3979 if (!strcmp(msg->id, ph->id)) {
3980 if (msg->group != NULL) {
3981 memprintf(err, "SPOE message '%s' already belongs to "
3982 "the SPOE group '%s' declare at %s:%d",
3983 msg->id, msg->group->id,
3984 msg->group->conf.file,
3985 msg->group->conf.line);
3986 goto error;
3987 }
3988
3989 /* Scope for arguments are not checked for now. We will check
3990 * them only if a rule use the corresponding SPOE group. */
3991 msg->agent = curagent;
3992 msg->group = grp;
3993 LIST_DEL(&ph->list);
3994 LIST_ADDQ(&grp->messages, &msg->by_grp);
3995 goto next_mph_grp;
3996 }
3997 }
3998 memprintf(err, "SPOE group '%s' try to use undefined SPOE message '%s' at %s:%d",
3999 grp->id, ph->id, curagent->conf.file, curagent->conf.line);
4000 goto error;
4001 next_mph_grp:
4002 continue;
4003 }
4004 }
4005
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004006 finish:
Christopher Faulet11610f32017-09-21 10:23:10 +02004007 /* move curmsgs to the agent message list */
4008 curmsgs.n->p = &curagent->messages;
4009 curmsgs.p->n = &curagent->messages;
4010 curagent->messages = curmsgs;
4011 LIST_INIT(&curmsgs);
4012
Christopher Faulet7ee86672017-09-19 11:08:28 +02004013 conf->id = strdup(engine ? engine : curagent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004014 conf->agent = curagent;
Christopher Faulet11610f32017-09-21 10:23:10 +02004015 list_for_each_entry_safe(ph, phback, &curmphs, list) {
4016 LIST_DEL(&ph->list);
4017 spoe_release_placeholder(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004018 }
Christopher Faulet11610f32017-09-21 10:23:10 +02004019 list_for_each_entry_safe(ph, phback, &curgphs, list) {
4020 LIST_DEL(&ph->list);
4021 spoe_release_placeholder(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004022 }
Christopher Faulet11610f32017-09-21 10:23:10 +02004023 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
4024 LIST_DEL(&grp->list);
4025 spoe_release_group(grp);
4026 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004027 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01004028 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004029 fconf->ops = &spoe_ops;
4030 fconf->conf = conf;
4031 return 0;
4032
4033 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01004034 spoe_release_agent(curagent);
Christopher Faulet11610f32017-09-21 10:23:10 +02004035 list_for_each_entry_safe(ph, phback, &curmphs, list) {
4036 LIST_DEL(&ph->list);
4037 spoe_release_placeholder(ph);
4038 }
4039 list_for_each_entry_safe(ph, phback, &curgphs, list) {
4040 LIST_DEL(&ph->list);
4041 spoe_release_placeholder(ph);
4042 }
4043 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
4044 LIST_DEL(&grp->list);
4045 spoe_release_group(grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004046 }
4047 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4048 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004049 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004050 }
4051 free(conf);
4052 return -1;
4053}
4054
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004055/* Send message of a SPOE group. This is the action_ptr callback of a rule
4056 * associated to a "send-spoe-group" action.
4057 *
4058 * It returns ACT_RET_CONT is processing is finished without error, it returns
4059 * ACT_RET_YIELD if the action is in progress. Otherwise it returns
4060 * ACT_RET_ERR. */
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004061static enum act_return
4062spoe_send_group(struct act_rule *rule, struct proxy *px,
4063 struct session *sess, struct stream *s, int flags)
4064{
4065 struct filter *filter;
4066 struct spoe_agent *agent = NULL;
4067 struct spoe_group *group = NULL;
4068 struct spoe_context *ctx = NULL;
4069 int ret, dir;
4070
4071 list_for_each_entry(filter, &s->strm_flt.filters, list) {
4072 if (filter->config == rule->arg.act.p[0]) {
4073 agent = rule->arg.act.p[2];
4074 group = rule->arg.act.p[3];
4075 ctx = filter->ctx;
4076 break;
4077 }
4078 }
4079 if (agent == NULL || group == NULL || ctx == NULL)
4080 return ACT_RET_ERR;
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004081 if (ctx->state == SPOE_CTX_ST_NONE)
4082 return ACT_RET_CONT;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004083
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004084 switch (rule->from) {
4085 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
4086 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
4087 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
4088 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
4089 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
4090 default:
4091 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
4092 " - internal error while execute spoe-send-group\n",
4093 (int)now.tv_sec, (int)now.tv_usec, agent->id,
4094 __FUNCTION__, s);
4095 send_log(px, LOG_ERR, "SPOE: [%s] internal error while execute spoe-send-group\n",
4096 agent->id);
4097 return ACT_RET_CONT;
4098 }
4099
4100 ret = spoe_process_group(s, ctx, group, dir);
4101 if (ret == 1)
4102 return ACT_RET_CONT;
4103 else if (ret == 0) {
4104 if (flags & ACT_FLAG_FINAL) {
4105 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
4106 " - failed to process group '%s': interrupted by caller\n",
4107 (int)now.tv_sec, (int)now.tv_usec,
4108 agent->id, __FUNCTION__, s, group->id);
4109 ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
4110 spoe_handle_processing_error(s, agent, ctx, dir);
4111 spoe_stop_processing(ctx);
4112 return ACT_RET_CONT;
4113 }
4114 return ACT_RET_YIELD;
4115 }
4116 else
4117 return ACT_RET_ERR;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004118}
4119
4120/* Check an "send-spoe-group" action. Here, we'll try to find the real SPOE
4121 * group associated to <rule>. The format of an rule using 'send-spoe-group'
4122 * action should be:
4123 *
4124 * (http|tcp)-(request|response) send-spoe-group <engine-id> <group-id>
4125 *
4126 * So, we'll loop on each configured SPOE filter for the proxy <px> to find the
4127 * SPOE engine matching <engine-id>. And then, we'll try to find the good group
4128 * matching <group-id>. Finally, we'll check all messages referenced by the SPOE
4129 * group.
4130 *
4131 * The function returns 1 in success case, otherwise, it returns 0 and err is
4132 * filled.
4133 */
4134static int
4135check_send_spoe_group(struct act_rule *rule, struct proxy *px, char **err)
4136{
4137 struct flt_conf *fconf;
4138 struct spoe_config *conf;
4139 struct spoe_agent *agent = NULL;
4140 struct spoe_group *group;
4141 struct spoe_message *msg;
4142 char *engine_id = rule->arg.act.p[0];
4143 char *group_id = rule->arg.act.p[1];
4144 unsigned int where = 0;
4145
4146 switch (rule->from) {
4147 case ACT_F_TCP_REQ_SES: where = SMP_VAL_FE_SES_ACC; break;
4148 case ACT_F_TCP_REQ_CNT: where = SMP_VAL_FE_REQ_CNT; break;
4149 case ACT_F_TCP_RES_CNT: where = SMP_VAL_BE_RES_CNT; break;
4150 case ACT_F_HTTP_REQ: where = SMP_VAL_FE_HRQ_HDR; break;
4151 case ACT_F_HTTP_RES: where = SMP_VAL_BE_HRS_HDR; break;
4152 default:
4153 memprintf(err,
4154 "internal error, unexpected rule->from=%d, please report this bug!",
4155 rule->from);
4156 goto error;
4157 }
4158
4159 /* Try to find the SPOE engine by checking all SPOE filters for proxy
4160 * <px> */
4161 list_for_each_entry(fconf, &px->filter_configs, list) {
4162 conf = fconf->conf;
4163
4164 /* This is not an SPOE filter */
4165 if (fconf->id != spoe_filter_id)
4166 continue;
4167
4168 /* This is the good engine */
4169 if (!strcmp(conf->id, engine_id)) {
4170 agent = conf->agent;
4171 break;
4172 }
4173 }
4174 if (agent == NULL) {
4175 memprintf(err, "unable to find SPOE engine '%s' used by the send-spoe-group '%s'",
4176 engine_id, group_id);
4177 goto error;
4178 }
4179
4180 /* Try to find the right group */
4181 list_for_each_entry(group, &agent->groups, list) {
4182 /* This is the good group */
4183 if (!strcmp(group->id, group_id))
4184 break;
4185 }
4186 if (&group->list == &agent->groups) {
4187 memprintf(err, "unable to find SPOE group '%s' into SPOE engine '%s' configuration",
4188 group_id, engine_id);
4189 goto error;
4190 }
4191
4192 /* Ok, we found the group, we need to check messages and their
4193 * arguments */
4194 list_for_each_entry(msg, &group->messages, by_grp) {
4195 struct spoe_arg *arg;
4196
4197 list_for_each_entry(arg, &msg->args, list) {
4198 if (!(arg->expr->fetch->val & where)) {
4199 memprintf(err, "Invalid SPOE message '%s' used by SPOE group '%s' at %s:%d: "
4200 "some args extract information from '%s',"
4201 "none of which is available here ('%s')",
4202 msg->id, group->id, msg->conf.file, msg->conf.line,
4203 sample_ckp_names(arg->expr->fetch->use),
4204 sample_ckp_names(where));
4205 goto error;
4206 }
4207 }
4208 }
4209
4210 free(engine_id);
4211 free(group_id);
4212 rule->arg.act.p[0] = fconf; /* Associate filter config with the rule */
4213 rule->arg.act.p[1] = conf; /* Associate SPOE config with the rule */
4214 rule->arg.act.p[2] = agent; /* Associate SPOE agent with the rule */
4215 rule->arg.act.p[3] = group; /* Associate SPOE group with the rule */
4216 return 1;
4217
4218 error:
4219 free(engine_id);
4220 free(group_id);
4221 return 0;
4222}
4223
4224/* Parse 'send-spoe-group' action following the format:
4225 *
4226 * ... send-spoe-group <engine-id> <group-id>
4227 *
4228 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
4229 * message. Otherwise, it returns ACT_RET_PRS_OK and parsing engine and group
4230 * ids are saved and used later, when the rule will be checked.
4231 */
4232static enum act_parse_ret
4233parse_send_spoe_group(const char **args, int *orig_arg, struct proxy *px,
4234 struct act_rule *rule, char **err)
4235{
4236 if (!*args[*orig_arg] || !*args[*orig_arg+1] ||
4237 (*args[*orig_arg+2] && strcmp(args[*orig_arg+2], "if") != 0 && strcmp(args[*orig_arg+2], "unless") != 0)) {
4238 memprintf(err, "expects 2 arguments: <engine-id> <group-id>");
4239 return ACT_RET_PRS_ERR;
4240 }
4241 rule->arg.act.p[0] = strdup(args[*orig_arg]); /* Copy the SPOE engine id */
4242 rule->arg.act.p[1] = strdup(args[*orig_arg+1]); /* Cope the SPOE group id */
4243
4244 (*orig_arg) += 2;
4245
4246 rule->action = ACT_CUSTOM;
4247 rule->action_ptr = spoe_send_group;
4248 rule->check_ptr = check_send_spoe_group;
4249 return ACT_RET_PRS_OK;
4250}
4251
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004252
4253/* Declare the filter parser for "spoe" keyword */
4254static struct flt_kw_list flt_kws = { "SPOE", { }, {
4255 { "spoe", parse_spoe_flt, NULL },
4256 { NULL, NULL, NULL },
4257 }
4258};
4259
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004260/* Delcate the action parser for "spoe-action" keyword */
4261static struct action_kw_list tcp_req_action_kws = { { }, {
4262 { "send-spoe-group", parse_send_spoe_group },
4263 { /* END */ },
4264 }
4265};
4266static struct action_kw_list tcp_res_action_kws = { { }, {
4267 { "send-spoe-group", parse_send_spoe_group },
4268 { /* END */ },
4269 }
4270};
4271static struct action_kw_list http_req_action_kws = { { }, {
4272 { "send-spoe-group", parse_send_spoe_group },
4273 { /* END */ },
4274 }
4275};
4276static struct action_kw_list http_res_action_kws = { { }, {
4277 { "send-spoe-group", parse_send_spoe_group },
4278 { /* END */ },
4279 }
4280};
4281
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004282__attribute__((constructor))
4283static void __spoe_init(void)
4284{
4285 flt_register_keywords(&flt_kws);
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004286 tcp_req_cont_keywords_register(&tcp_req_action_kws);
4287 tcp_res_cont_keywords_register(&tcp_res_action_kws);
4288 http_req_keywords_register(&http_req_action_kws);
4289 http_res_keywords_register(&http_res_action_kws);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004290
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004291 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004292 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004293}
4294
4295__attribute__((destructor))
4296static void
4297__spoe_deinit(void)
4298{
4299 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004300 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004301}