blob: 1b04598f7869986a0ff3d73d9629dfd158195d0b [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
15#include <common/buffer.h>
16#include <common/cfgparse.h>
17#include <common/compat.h>
18#include <common/config.h>
19#include <common/debug.h>
20#include <common/memory.h>
21#include <common/time.h>
22
23#include <types/arg.h>
24#include <types/filters.h>
25#include <types/global.h>
26#include <types/proxy.h>
27#include <types/sample.h>
28#include <types/stream.h>
29
30#include <proto/arg.h>
31#include <proto/backend.h>
32#include <proto/filters.h>
Christopher Faulet48026722016-11-16 15:01:12 +010033#include <proto/freq_ctr.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020034#include <proto/frontend.h>
35#include <proto/log.h>
36#include <proto/proto_http.h>
37#include <proto/proxy.h>
38#include <proto/sample.h>
39#include <proto/session.h>
40#include <proto/signal.h>
41#include <proto/stream.h>
42#include <proto/stream_interface.h>
43#include <proto/task.h>
44#include <proto/vars.h>
45
46#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
47#define SPOE_PRINTF(x...) fprintf(x)
48#else
49#define SPOE_PRINTF(x...)
50#endif
51
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020052/* Minimal size for a frame */
53#define MIN_FRAME_SIZE 256
54
Christopher Fauletea62c2a2016-11-14 10:54:21 +010055/* Flags set on the SPOE agent */
56#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */
57
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020058/* Flags set on the SPOE context */
59#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
60#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
61#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */
62#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */
63
64#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
65
Christopher Fauleta1cda022016-12-21 08:58:06 +010066/* Flags set on the SPOE applet */
67#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */
68#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */
69#define SPOE_APPCTX_FL_PERSIST 0x00000004 /* Set if the applet is persistent */
70
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020071#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */
72#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */
73
74/* All possible states for a SPOE context */
75enum spoe_ctx_state {
76 SPOE_CTX_ST_NONE = 0,
77 SPOE_CTX_ST_READY,
78 SPOE_CTX_ST_SENDING_MSGS,
79 SPOE_CTX_ST_WAITING_ACK,
80 SPOE_CTX_ST_DONE,
81 SPOE_CTX_ST_ERROR,
82};
83
84/* All possible states for a SPOE applet */
85enum spoe_appctx_state {
86 SPOE_APPCTX_ST_CONNECT = 0,
87 SPOE_APPCTX_ST_CONNECTING,
Christopher Fauleta1cda022016-12-21 08:58:06 +010088 SPOE_APPCTX_ST_IDLE,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020089 SPOE_APPCTX_ST_PROCESSING,
90 SPOE_APPCTX_ST_DISCONNECT,
91 SPOE_APPCTX_ST_DISCONNECTING,
92 SPOE_APPCTX_ST_EXIT,
93 SPOE_APPCTX_ST_END,
94};
95
96/* All supported SPOE actions */
97enum spoe_action_type {
98 SPOE_ACT_T_SET_VAR = 1,
99 SPOE_ACT_T_UNSET_VAR,
100 SPOE_ACT_TYPES,
101};
102
103/* All supported SPOE events */
104enum spoe_event {
105 SPOE_EV_NONE = 0,
106
107 /* Request events */
108 SPOE_EV_ON_CLIENT_SESS = 1,
109 SPOE_EV_ON_TCP_REQ_FE,
110 SPOE_EV_ON_TCP_REQ_BE,
111 SPOE_EV_ON_HTTP_REQ_FE,
112 SPOE_EV_ON_HTTP_REQ_BE,
113
114 /* Response events */
115 SPOE_EV_ON_SERVER_SESS,
116 SPOE_EV_ON_TCP_RSP,
117 SPOE_EV_ON_HTTP_RSP,
118
119 SPOE_EV_EVENTS
120};
121
Christopher Fauletb067b062017-01-04 16:39:11 +0100122/* Errors triggered by streams */
123enum spoe_context_error {
124 SPOE_CTX_ERR_NONE = 0,
125 SPOE_CTX_ERR_TOUT,
126 SPOE_CTX_ERR_RES,
127 SPOE_CTX_ERR_UNKNOWN = 255,
128 SPOE_CTX_ERRS,
129};
130
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200131/* Errors triggerd by SPOE applet */
132enum spoe_frame_error {
133 SPOE_FRM_ERR_NONE = 0,
134 SPOE_FRM_ERR_IO,
135 SPOE_FRM_ERR_TOUT,
136 SPOE_FRM_ERR_TOO_BIG,
137 SPOE_FRM_ERR_INVALID,
138 SPOE_FRM_ERR_NO_VSN,
139 SPOE_FRM_ERR_NO_FRAME_SIZE,
140 SPOE_FRM_ERR_NO_CAP,
141 SPOE_FRM_ERR_BAD_VSN,
142 SPOE_FRM_ERR_BAD_FRAME_SIZE,
143 SPOE_FRM_ERR_UNKNOWN = 99,
144 SPOE_FRM_ERRS,
145};
146
147/* Scopes used for variables set by agents. It is a way to be agnotic to vars
148 * scope. */
149enum spoe_vars_scope {
150 SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
151 SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
152 SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
153 SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
154 SPOE_SCOPE_RES, /* <=> SCOPE_RES */
155};
156
157
158/* Describe an argument that will be linked to a message. It is a sample fetch,
159 * with an optional name. */
160struct spoe_arg {
161 char *name; /* Name of the argument, may be NULL */
162 unsigned int name_len; /* The name length, 0 if NULL */
163 struct sample_expr *expr; /* Sample expression */
164 struct list list; /* Used to chain SPOE args */
165};
166
167/* Used during the config parsing only because, when a SPOE agent section is
168 * parsed, messages can be undefined. */
169struct spoe_msg_placeholder {
170 char *id; /* SPOE message placeholder id */
171 struct list list; /* Use to chain SPOE message placeholders */
172};
173
174/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
175 * an argument list (see above) and it is linked to a specific event. */
176struct spoe_message {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100177 char *id; /* SPOE message id */
178 unsigned int id_len; /* The message id length */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200179 struct spoe_agent *agent; /* SPOE agent owning this SPOE message */
180 struct {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100181 char *file; /* file where the SPOE message appears */
182 int line; /* line where the SPOE message appears */
183 } conf; /* config information */
184 struct list args; /* Arguments added when the SPOE messages is sent */
185 struct list list; /* Used to chain SPOE messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200186
187 enum spoe_event event; /* SPOE_EV_* */
188};
189
190/* Describe a SPOE agent. */
191struct spoe_agent {
192 char *id; /* SPOE agent id (name) */
193 struct {
194 char *file; /* file where the SPOE agent appears */
195 int line; /* line where the SPOE agent appears */
196 } conf; /* config information */
197 union {
198 struct proxy *be; /* Backend used by this agent */
199 char *name; /* Backend name used during conf parsing */
200 } b;
201 struct {
Christopher Fauletf7a30922016-11-10 15:04:51 +0100202 unsigned int hello; /* Max time to receive AGENT-HELLO frame (in SPOE applet) */
203 unsigned int idle; /* Max Idle timeout (in SPOE applet) */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100204 unsigned int processing; /* Max time to process an event (in the main stream) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200205 } timeout;
206
Christopher Fauleta1cda022016-12-21 08:58:06 +0100207 /* Config info */
208 char *engine_id; /* engine-id string */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200209 char *var_pfx; /* Prefix used for vars set by the agent */
Christopher Faulet985532d2016-11-16 15:36:19 +0100210 char *var_on_error; /* Variable to set when an error occured, in the TXN scope */
Christopher Fauletea62c2a2016-11-14 10:54:21 +0100211 unsigned int flags; /* SPOE_FL_* */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100212 unsigned int cps_max; /* Maximum # of connections per second */
213 unsigned int eps_max; /* Maximum # of errors per second */
214 unsigned int max_frame_size; /* Maximum frame size for this agent, before any negotiation */
215 unsigned int min_applets; /* Minimum # applets alive at a time */
216 unsigned int max_fpa; /* Maximum # of frames handled per applet at once */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200217
218 struct list messages[SPOE_EV_EVENTS]; /* List of SPOE messages that will be sent
219 * for each supported events */
220
Christopher Fauleta1cda022016-12-21 08:58:06 +0100221 /* running info */
222 unsigned int applets_act; /* # of applets alive at a time */
223 unsigned int applets_idle; /* # of applets in the state SPOE_APPCTX_ST_IDLE */
224 unsigned int sending_rate; /* the global sending rate */
225
226 struct freq_ctr conn_per_sec; /* connections per second */
227 struct freq_ctr err_per_sec; /* connetion errors per second */
228
229 struct list applets; /* List of available SPOE applets */
230 struct list sending_queue; /* Queue of streams waiting to send data */
231 struct list waiting_queue; /* Queue of streams waiting for a ack, in async mode */
232
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200233};
234
235/* SPOE filter configuration */
236struct spoe_config {
237 struct proxy *proxy; /* Proxy owning the filter */
238 struct spoe_agent *agent; /* Agent used by this filter */
239 struct proxy agent_fe; /* Agent frontend */
240};
241
242/* SPOE context attached to a stream. It is the main structure that handles the
243 * processing offload */
244struct spoe_context {
245 struct filter *filter; /* The SPOE filter */
246 struct stream *strm; /* The stream that should be offloaded */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100247
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200248 struct list *messages; /* List of messages that will be sent during the stream processing */
249 struct buffer *buffer; /* Buffer used to store a NOTIFY or ACK frame */
Christopher Fauleta73e59b2016-12-09 17:30:18 +0100250 struct buffer_wait buffer_wait; /* position in the list of streams waiting for a buffer */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100251 struct list list;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200252
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200253 enum spoe_ctx_state state; /* SPOE_CTX_ST_* */
254 unsigned int flags; /* SPOE_CTX_FL_* */
Christopher Fauletb067b062017-01-04 16:39:11 +0100255 unsigned int status_code; /* SPOE_CTX_ERR_* */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200256
257 unsigned int stream_id; /* stream_id and frame_id are used */
258 unsigned int frame_id; /* to map NOTIFY and ACK frames */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100259 unsigned int process_exp; /* expiration date to process an event */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200260};
261
Christopher Faulet42bfa462017-01-04 14:14:19 +0100262/* SPOE context inside a appctx */
263struct spoe_appctx {
264 struct appctx *owner; /* the owner */
265 struct task *task; /* task to handle applet timeouts */
266 struct spoe_agent *agent; /* agent on which the applet is attached */
267
268 unsigned int version; /* the negotiated version */
269 unsigned int max_frame_size; /* the negotiated max-frame-size value */
270 unsigned int flags; /* SPOE_APPCTX_FL_* */
271
Christopher Fauletb067b062017-01-04 16:39:11 +0100272 unsigned int status_code; /* SPOE_FRM_ERR_* */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100273 struct list waiting_queue; /* list of streams waiting for a ACK frame, in sync and pipelining mode */
274 struct list list; /* next spoe appctx for the same agent */
275};
276
277#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
278
Christopher Faulet3b386a32017-02-23 10:17:15 +0100279/* SPOE filter id. Used to identify SPOE filters */
280const char *spoe_filter_id = "SPOE filter";
281
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200282/* Set if the handle on SIGUSR1 is registered */
283static int sighandler_registered = 0;
284
285/* proxy used during the parsing */
286struct proxy *curproxy = NULL;
287
288/* The name of the SPOE engine, used during the parsing */
289char *curengine = NULL;
290
291/* SPOE agent used during the parsing */
292struct spoe_agent *curagent = NULL;
293
294/* SPOE message used during the parsing */
295struct spoe_message *curmsg = NULL;
296
297/* list of SPOE messages and placeholders used during the parsing */
298struct list curmsgs;
299struct list curmps;
300
Christopher Faulet42bfa462017-01-04 14:14:19 +0100301/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200302static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100303static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200304
305/* Temporary variables used to ease error processing */
306int spoe_status_code = SPOE_FRM_ERR_NONE;
307char spoe_reason[256];
308
309struct flt_ops spoe_ops;
310
Christopher Fauleta1cda022016-12-21 08:58:06 +0100311static int queue_spoe_context(struct spoe_context *ctx);
312static int acquire_spoe_buffer(struct spoe_context *ctx);
313static void release_spoe_buffer(struct spoe_context *ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200314
315/********************************************************************
316 * helper functions/globals
317 ********************************************************************/
318static void
319release_spoe_msg_placeholder(struct spoe_msg_placeholder *mp)
320{
321 if (!mp)
322 return;
323 free(mp->id);
324 free(mp);
325}
326
327
328static void
329release_spoe_message(struct spoe_message *msg)
330{
331 struct spoe_arg *arg, *back;
332
333 if (!msg)
334 return;
335 free(msg->id);
336 free(msg->conf.file);
337 list_for_each_entry_safe(arg, back, &msg->args, list) {
338 release_sample_expr(arg->expr);
339 free(arg->name);
340 LIST_DEL(&arg->list);
341 free(arg);
342 }
343 free(msg);
344}
345
346static void
347release_spoe_agent(struct spoe_agent *agent)
348{
349 struct spoe_message *msg, *back;
350 int i;
351
352 if (!agent)
353 return;
354 free(agent->id);
355 free(agent->conf.file);
356 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100357 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100358 free(agent->var_on_error);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200359 for (i = 0; i < SPOE_EV_EVENTS; ++i) {
360 list_for_each_entry_safe(msg, back, &agent->messages[i], list) {
361 LIST_DEL(&msg->list);
362 release_spoe_message(msg);
363 }
364 }
365 free(agent);
366}
367
368static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
369 [SPOE_FRM_ERR_NONE] = "normal",
370 [SPOE_FRM_ERR_IO] = "I/O error",
371 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
372 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
373 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
374 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
375 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
376 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
377 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
378 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
379 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
380};
381
382static const char *spoe_event_str[SPOE_EV_EVENTS] = {
383 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
384 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
385 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
386 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
387 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
388
389 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
390 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
391 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
392};
393
394
395#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
396
397static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
398 [SPOE_CTX_ST_NONE] = "NONE",
399 [SPOE_CTX_ST_READY] = "READY",
400 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
401 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
402 [SPOE_CTX_ST_DONE] = "DONE",
403 [SPOE_CTX_ST_ERROR] = "ERROR",
404};
405
406static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
407 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
408 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
Christopher Fauleta1cda022016-12-21 08:58:06 +0100409 [SPOE_APPCTX_ST_IDLE] = "IDLE",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200410 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
411 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
412 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
413 [SPOE_APPCTX_ST_EXIT] = "EXIT",
414 [SPOE_APPCTX_ST_END] = "END",
415};
416
417#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100418
419static char *
420generate_pseudo_uuid()
421{
422 static int init = 0;
423
424 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
425 const char uuid_chr[] = "0123456789ABCDEF-";
426 char *uuid;
427 int i;
428
429 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
430 return NULL;
431
432 if (!init) {
433 srand(now_ms);
434 init = 1;
435 }
436
437 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
438 int r = rand () % 16;
439
440 switch (uuid_fmt[i]) {
441 case 'x' : uuid[i] = uuid_chr[r]; break;
442 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
443 default : uuid[i] = uuid_fmt[i]; break;
444 }
445 }
446 return uuid;
447}
448
449static inline unsigned int
450min_applets_act(struct spoe_agent *agent)
451{
452 unsigned int nbsrv;
453
454 if (agent->min_applets)
455 return agent->min_applets;
456
457 nbsrv = (agent->b.be->srv_act ? agent->b.be->srv_act : agent->b.be->srv_bck);
458 return 2*nbsrv;
459}
460
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200461/********************************************************************
462 * Functions that encode/decode SPOE frames
463 ********************************************************************/
464/* Frame Types sent by HAProxy and by agents */
465enum spoe_frame_type {
466 /* Frames sent by HAProxy */
467 SPOE_FRM_T_HAPROXY_HELLO = 1,
468 SPOE_FRM_T_HAPROXY_DISCON,
469 SPOE_FRM_T_HAPROXY_NOTIFY,
470
471 /* Frames sent by the agents */
472 SPOE_FRM_T_AGENT_HELLO = 101,
473 SPOE_FRM_T_AGENT_DISCON,
474 SPOE_FRM_T_AGENT_ACK
475};
476
477/* All supported data types */
478enum spoe_data_type {
479 SPOE_DATA_T_NULL = 0,
480 SPOE_DATA_T_BOOL,
481 SPOE_DATA_T_INT32,
482 SPOE_DATA_T_UINT32,
483 SPOE_DATA_T_INT64,
484 SPOE_DATA_T_UINT64,
485 SPOE_DATA_T_IPV4,
486 SPOE_DATA_T_IPV6,
487 SPOE_DATA_T_STR,
488 SPOE_DATA_T_BIN,
489 SPOE_DATA_TYPES
490};
491
492/* Masks to get data type or flags value */
493#define SPOE_DATA_T_MASK 0x0F
494#define SPOE_DATA_FL_MASK 0xF0
495
496/* Flags to set Boolean values */
497#define SPOE_DATA_FL_FALSE 0x00
498#define SPOE_DATA_FL_TRUE 0x10
499
500/* Helper to get static string length, excluding the terminating null byte */
501#define SLEN(str) (sizeof(str)-1)
502
503/* Predefined key used in HELLO/DISCONNECT frames */
504#define SUPPORTED_VERSIONS_KEY "supported-versions"
505#define VERSION_KEY "version"
506#define MAX_FRAME_SIZE_KEY "max-frame-size"
507#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100508#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100509#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200510#define STATUS_CODE_KEY "status-code"
511#define MSG_KEY "message"
512
513struct spoe_version {
514 char *str;
515 int min;
516 int max;
517};
518
519/* All supported versions */
520static struct spoe_version supported_versions[] = {
521 {"1.0", 1000, 1000},
522 {NULL, 0, 0}
523};
524
525/* Comma-separated list of supported versions */
526#define SUPPORTED_VERSIONS_VAL "1.0"
527
528/* Comma-separated list of supported capabilities (none for now) */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100529//#define CAPABILITIES_VAL ""
530#define CAPABILITIES_VAL "pipelining,async"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200531
532static int
533decode_spoe_version(const char *str, size_t len)
534{
535 char tmp[len+1], *start, *end;
536 double d;
537 int vsn = -1;
538
539 memset(tmp, 0, len+1);
540 memcpy(tmp, str, len);
541
542 start = tmp;
543 while (isspace(*start))
544 start++;
545
546 d = strtod(start, &end);
547 if (d == 0 || start == end)
548 goto out;
549
550 if (*end) {
551 while (isspace(*end))
552 end++;
553 if (*end)
554 goto out;
555 }
556 vsn = (int)(d * 1000);
557 out:
558 return vsn;
559}
560
561/* Encode a variable-length integer. This function never fails and returns the
562 * number of written bytes. */
563static int
564encode_spoe_varint(uint64_t i, char *buf)
565{
566 int idx;
567
568 if (i < 240) {
569 buf[0] = (unsigned char)i;
570 return 1;
571 }
572
573 buf[0] = (unsigned char)i | 240;
574 i = (i - 240) >> 4;
575 for (idx = 1; i >= 128; ++idx) {
576 buf[idx] = (unsigned char)i | 128;
577 i = (i - 128) >> 7;
578 }
579 buf[idx++] = (unsigned char)i;
580 return idx;
581}
582
583/* Decode a varable-length integer. If the decoding fails, -1 is returned. This
584 * happens when the buffer's end in reached. On success, the number of read
585 * bytes is returned. */
586static int
587decode_spoe_varint(const char *buf, const char *end, uint64_t *i)
588{
589 unsigned char *msg = (unsigned char *)buf;
590 int idx = 0;
591
592 if (msg > (unsigned char *)end)
593 return -1;
594
595 if (msg[0] < 240) {
596 *i = msg[0];
597 return 1;
598 }
599 *i = msg[0];
600 do {
601 ++idx;
602 if (msg+idx > (unsigned char *)end)
603 return -1;
604 *i += (uint64_t)msg[idx] << (4 + 7 * (idx-1));
605 } while (msg[idx] >= 128);
606 return (idx + 1);
607}
608
609/* Encode a string. The string will be prefix by its length, encoded as a
610 * variable-length integer. This function never fails and returns the number of
611 * written bytes. */
612static int
613encode_spoe_string(const char *str, size_t len, char *dst)
614{
615 int idx = 0;
616
617 if (!len) {
618 dst[0] = 0;
619 return 1;
620 }
621
622 idx += encode_spoe_varint(len, dst);
623 memcpy(dst+idx, str, len);
624 return (idx + len);
625}
626
627/* Decode a string. Its length is decoded first as a variable-length integer. If
628 * it succeeds, and if the string length is valid, the begin of the string is
629 * saved in <*str>, its length is saved in <*len> and the total numbre of bytes
630 * read is returned. If an error occurred, -1 is returned and <*str> remains
631 * NULL. */
632static int
633decode_spoe_string(char *buf, char *end, char **str, uint64_t *len)
634{
635 int i, idx = 0;
636
637 *str = NULL;
638 *len = 0;
639
640 if ((i = decode_spoe_varint(buf, end, len)) == -1)
641 goto error;
642 idx += i;
643 if (buf + idx + *len > end)
644 goto error;
645
646 *str = buf+idx;
647 return (idx + *len);
648
649 error:
650 return -1;
651}
652
653/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
654 * of bytes read is returned. A types data is composed of a type (1 byte) and
655 * corresponding data:
656 * - boolean: non additional data (0 bytes)
657 * - integers: a variable-length integer (see decode_spoe_varint)
658 * - ipv4: 4 bytes
659 * - ipv6: 16 bytes
660 * - binary and string: a buffer prefixed by its size, a variable-length
661 * integer (see decode_spoe_string) */
662static int
663skip_spoe_data(char *frame, char *end)
664{
665 uint64_t sz = 0;
666 int i, idx = 0;
667
668 if (frame > end)
669 return -1;
670
671 switch (frame[idx++] & SPOE_DATA_T_MASK) {
672 case SPOE_DATA_T_BOOL:
673 break;
674 case SPOE_DATA_T_INT32:
675 case SPOE_DATA_T_INT64:
676 case SPOE_DATA_T_UINT32:
677 case SPOE_DATA_T_UINT64:
678 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
679 return -1;
680 idx += i;
681 break;
682 case SPOE_DATA_T_IPV4:
683 idx += 4;
684 break;
685 case SPOE_DATA_T_IPV6:
686 idx += 16;
687 break;
688 case SPOE_DATA_T_STR:
689 case SPOE_DATA_T_BIN:
690 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
691 return -1;
692 idx += i + sz;
693 break;
694 }
695
696 if (frame+idx > end)
697 return -1;
698 return idx;
699}
700
701/* Decode a typed data. If an error occurred, -1 is returned, otherwise the
702 * number of read bytes is returned. See skip_spoe_data for details. */
703static int
704decode_spoe_data(char *frame, char *end, struct sample *smp)
705{
706 uint64_t sz = 0;
707 int type, i, idx = 0;
708
709 if (frame > end)
710 return -1;
711
712 type = frame[idx++];
713 switch (type & SPOE_DATA_T_MASK) {
714 case SPOE_DATA_T_BOOL:
715 smp->data.u.sint = ((type & SPOE_DATA_FL_TRUE) == SPOE_DATA_FL_TRUE);
716 smp->data.type = SMP_T_BOOL;
717 break;
718 case SPOE_DATA_T_INT32:
719 case SPOE_DATA_T_INT64:
720 case SPOE_DATA_T_UINT32:
721 case SPOE_DATA_T_UINT64:
722 if ((i = decode_spoe_varint(frame+idx, end, (uint64_t *)&smp->data.u.sint)) == -1)
723 return -1;
724 idx += i;
725 smp->data.type = SMP_T_SINT;
726 break;
727 case SPOE_DATA_T_IPV4:
728 if (frame+idx+4 > end)
729 return -1;
730 memcpy(&smp->data.u.ipv4, frame+idx, 4);
731 smp->data.type = SMP_T_IPV4;
732 idx += 4;
733 break;
734 case SPOE_DATA_T_IPV6:
735 if (frame+idx+16 > end)
736 return -1;
737 memcpy(&smp->data.u.ipv6, frame+idx, 16);
738 smp->data.type = SMP_T_IPV6;
739 idx += 16;
740 break;
741 case SPOE_DATA_T_STR:
742 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
743 return -1;
744 idx += i;
745 if (frame+idx+sz > end)
746 return -1;
747 smp->data.u.str.str = frame+idx;
748 smp->data.u.str.len = sz;
749 smp->data.type = SMP_T_STR;
750 idx += sz;
751 break;
752 case SPOE_DATA_T_BIN:
753 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
754 return -1;
755 idx += i;
756 if (frame+idx+sz > end)
757 return -1;
758 smp->data.u.str.str = frame+idx;
759 smp->data.u.str.len = sz;
760 smp->data.type = SMP_T_BIN;
761 idx += sz;
762 break;
763 }
764
765 if (frame+idx > end)
766 return -1;
767 return idx;
768}
769
770/* Skip an action in a frame received from an agent. If an error occurred, -1 is
771 * returned, otherwise the number of read bytes is returned. An action is
772 * composed of the action type followed by a typed data. */
773static int
774skip_spoe_action(char *frame, char *end)
775{
776 int n, i, idx = 0;
777
778 if (frame+2 > end)
779 return -1;
780
781 idx++; /* Skip the action type */
782 n = frame[idx++];
783 while (n-- > 0) {
784 if ((i = skip_spoe_data(frame+idx, end)) == -1)
785 return -1;
786 idx += i;
787 }
788
789 if (frame+idx > end)
790 return -1;
791 return idx;
792}
793
794/* Encode HELLO frame sent by HAProxy to an agent. It returns the frame size on
795 * success, 0 if the frame can be ignored and -1 if an error occurred. */
796static int
797prepare_spoe_hahello_frame(struct appctx *appctx, char *frame, size_t size)
798{
Christopher Faulet42bfa462017-01-04 14:14:19 +0100799 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200800 int idx = 0;
801 size_t max = (7 /* TYPE + METADATA */
802 + 1 + SLEN(SUPPORTED_VERSIONS_KEY) + 1 + 1 + SLEN(SUPPORTED_VERSIONS_VAL)
803 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 4
Christopher Fauleta1cda022016-12-21 08:58:06 +0100804 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + SLEN(CAPABILITIES_VAL)
805 + 1 + SLEN(ENGINE_ID_KEY) + 1 + 1 + 36);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200806
Christopher Fauletb067b062017-01-04 16:39:11 +0100807 if (size < max) {
808 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200809 return -1;
Christopher Fauletb067b062017-01-04 16:39:11 +0100810 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200811
812 /* Frame type */
813 frame[idx++] = SPOE_FRM_T_HAPROXY_HELLO;
814
815 /* No flags for now */
816 memset(frame+idx, 0, 4);
817 idx += 4;
818
819 /* No stream-id and frame-id for HELLO frames */
820 frame[idx++] = 0;
821 frame[idx++] = 0;
822
823 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
824 * and "capabilities" */
825
826 /* "supported-versions" K/V item */
827 idx += encode_spoe_string(SUPPORTED_VERSIONS_KEY, SLEN(SUPPORTED_VERSIONS_KEY), frame+idx);
828 frame[idx++] = SPOE_DATA_T_STR;
829 idx += encode_spoe_string(SUPPORTED_VERSIONS_VAL, SLEN(SUPPORTED_VERSIONS_VAL), frame+idx);
830
831 /* "max-fram-size" K/V item */
832 idx += encode_spoe_string(MAX_FRAME_SIZE_KEY, SLEN(MAX_FRAME_SIZE_KEY), frame+idx);
833 frame[idx++] = SPOE_DATA_T_UINT32;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100834 idx += encode_spoe_varint(SPOE_APPCTX(appctx)->max_frame_size, frame+idx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200835
836 /* "capabilities" K/V item */
837 idx += encode_spoe_string(CAPABILITIES_KEY, SLEN(CAPABILITIES_KEY), frame+idx);
838 frame[idx++] = SPOE_DATA_T_STR;
839 idx += encode_spoe_string(CAPABILITIES_VAL, SLEN(CAPABILITIES_VAL), frame+idx);
840
Christopher Fauleta1cda022016-12-21 08:58:06 +0100841 /* "engine-id" K/V item */
842 if (agent != NULL && agent->engine_id != NULL) {
843 idx += encode_spoe_string(ENGINE_ID_KEY, SLEN(ENGINE_ID_KEY), frame+idx);
844 frame[idx++] = SPOE_DATA_T_STR;
845 idx += encode_spoe_string(agent->engine_id, strlen(agent->engine_id), frame+idx);
846 }
847
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200848 return idx;
849}
850
851/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the frame
852 * size on success, 0 if the frame can be ignored and -1 if an error
853 * occurred. */
854static int
855prepare_spoe_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
856{
857 const char *reason;
858 int rlen, idx = 0;
859 size_t max = (7 /* TYPE + METADATA */
860 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 2
861 + 1 + SLEN(MSG_KEY) + 1 + 2 + 255);
862
863 if (size < max)
864 return -1;
865
866 /* Get the message corresponding to the status code */
867 if (spoe_status_code >= SPOE_FRM_ERRS)
868 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
869 reason = spoe_frm_err_reasons[spoe_status_code];
870 rlen = strlen(reason);
871
872 /* Frame type */
873 frame[idx++] = SPOE_FRM_T_HAPROXY_DISCON;
874
875 /* No flags for now */
876 memset(frame+idx, 0, 4);
877 idx += 4;
878
879 /* No stream-id and frame-id for DISCONNECT frames */
880 frame[idx++] = 0;
881 frame[idx++] = 0;
882
883 /* There are 2 mandatory items: "status-code" and "message" */
884
885 /* "status-code" K/V item */
886 idx += encode_spoe_string(STATUS_CODE_KEY, SLEN(STATUS_CODE_KEY), frame+idx);
887 frame[idx++] = SPOE_DATA_T_UINT32;
888 idx += encode_spoe_varint(spoe_status_code, frame+idx);
889
890 /* "message" K/V item */
891 idx += encode_spoe_string(MSG_KEY, SLEN(MSG_KEY), frame+idx);
892 frame[idx++] = SPOE_DATA_T_STR;
893 idx += encode_spoe_string(reason, rlen, frame+idx);
894
895 return idx;
896}
897
898/* Encode NOTIFY frame sent by HAProxy to an agent. It returns the frame size on
899 * success, 0 if the frame can be ignored and -1 if an error occurred. */
900static int
Christopher Fauleta1cda022016-12-21 08:58:06 +0100901prepare_spoe_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
902 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200903{
Christopher Fauleta1cda022016-12-21 08:58:06 +0100904 int idx = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200905
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200906 frame[idx++] = SPOE_FRM_T_HAPROXY_NOTIFY;
907
908 /* No flags for now */
909 memset(frame+idx, 0, 4);
910 idx += 4;
911
912 /* Set stream-id and frame-id */
913 idx += encode_spoe_varint(ctx->stream_id, frame+idx);
914 idx += encode_spoe_varint(ctx->frame_id, frame+idx);
915
916 /* Copy encoded messages */
Christopher Fauletb067b062017-01-04 16:39:11 +0100917 if (idx + ctx->buffer->i > size) {
918 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100919 return 0;
Christopher Fauletb067b062017-01-04 16:39:11 +0100920 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100921
922 /* Copy encoded messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200923 memcpy(frame+idx, ctx->buffer->p, ctx->buffer->i);
924 idx += ctx->buffer->i;
925
926 return idx;
927}
928
929/* Decode HELLO frame sent by an agent. It returns the number of by read bytes
930 * on success, 0 if the frame can be ignored and -1 if an error occurred. */
931static int
932handle_spoe_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
933{
Christopher Fauleta1cda022016-12-21 08:58:06 +0100934 int vsn, max_frame_size, flags;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200935 int i, idx = 0;
936 size_t min_size = (7 /* TYPE + METADATA */
937 + 1 + SLEN(VERSION_KEY) + 1 + 1 + 3
938 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 1
939 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + 0);
940
941 /* Check frame type */
942 if (frame[idx++] != SPOE_FRM_T_AGENT_HELLO)
943 return 0;
944
945 if (size < min_size) {
946 spoe_status_code = SPOE_FRM_ERR_INVALID;
947 return -1;
948 }
949
950 /* Skip flags: fragmentation is not supported for now */
951 idx += 4;
952
953 /* stream-id and frame-id must be cleared */
954 if (frame[idx] != 0 || frame[idx+1] != 0) {
955 spoe_status_code = SPOE_FRM_ERR_INVALID;
956 return -1;
957 }
958 idx += 2;
959
960 /* There are 3 mandatory items: "version", "max-frame-size" and
961 * "capabilities" */
962
963 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100964 vsn = max_frame_size = flags = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200965 while (idx < size) {
966 char *str;
967 uint64_t sz;
968
969 /* Decode the item key */
970 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
971 if (str == NULL) {
972 spoe_status_code = SPOE_FRM_ERR_INVALID;
973 return -1;
974 }
975 /* Check "version" K/V item */
976 if (!memcmp(str, VERSION_KEY, sz)) {
977 /* The value must be a string */
978 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
979 spoe_status_code = SPOE_FRM_ERR_INVALID;
980 return -1;
981 }
982 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
983 if (str == NULL) {
984 spoe_status_code = SPOE_FRM_ERR_INVALID;
985 return -1;
986 }
987
988 vsn = decode_spoe_version(str, sz);
989 if (vsn == -1) {
990 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
991 return -1;
992 }
993 for (i = 0; supported_versions[i].str != NULL; ++i) {
994 if (vsn >= supported_versions[i].min &&
995 vsn <= supported_versions[i].max)
996 break;
997 }
998 if (supported_versions[i].str == NULL) {
999 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
1000 return -1;
1001 }
1002 }
1003 /* Check "max-frame-size" K/V item */
1004 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
1005 int type;
1006
1007 /* The value must be integer */
1008 type = frame[idx++];
1009 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1010 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1011 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1012 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1013 spoe_status_code = SPOE_FRM_ERR_INVALID;
1014 return -1;
1015 }
1016 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1017 spoe_status_code = SPOE_FRM_ERR_INVALID;
1018 return -1;
1019 }
1020 idx += i;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001021 if (sz < MIN_FRAME_SIZE || sz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001022 spoe_status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
1023 return -1;
1024 }
1025 max_frame_size = sz;
1026 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001027 /* Check "capabilities" K/V item */
1028 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
1029 int i;
1030
1031 /* The value must be a string */
1032 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1033 spoe_status_code = SPOE_FRM_ERR_INVALID;
1034 return -1;
1035 }
1036 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1037 if (str == NULL)
1038 continue;
1039
1040 i = 0;
1041 while (i < sz) {
1042 char *delim;
1043
1044 /* Skip leading spaces */
1045 for (; isspace(str[i]) && i < sz; i++);
1046
1047 if (sz - i >= 10 && !strncmp(str + i, "pipelining", 10)) {
1048 i += 10;
1049 if (sz == i || isspace(str[i]) || str[i] == ',')
1050 flags |= SPOE_APPCTX_FL_PIPELINING;
1051 }
1052 else if (sz - i >= 5 && !strncmp(str + i, "async", 5)) {
1053 i += 5;
1054 if (sz == i || isspace(str[i]) || str[i] == ',')
1055 flags |= SPOE_APPCTX_FL_ASYNC;
1056 }
1057
1058 if (sz == i || (delim = memchr(str + i, ',', sz-i)) == NULL)
1059 break;
1060 i = (delim - str) + 1;
1061 }
1062 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001063 else {
1064 /* Silently ignore unknown item */
1065 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1066 spoe_status_code = SPOE_FRM_ERR_INVALID;
1067 return -1;
1068 }
1069 idx += i;
1070 }
1071 }
1072
1073 /* Final checks */
1074 if (!vsn) {
1075 spoe_status_code = SPOE_FRM_ERR_NO_VSN;
1076 return -1;
1077 }
1078 if (!max_frame_size) {
1079 spoe_status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
1080 return -1;
1081 }
1082
Christopher Faulet42bfa462017-01-04 14:14:19 +01001083 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
1084 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
1085 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001086 return idx;
1087}
1088
1089/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
1090 * bytes on success, 0 if the frame can be ignored and -1 if an error
1091 * occurred. */
1092static int
1093handle_spoe_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
1094{
1095 int i, idx = 0;
1096 size_t min_size = (7 /* TYPE + METADATA */
1097 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 1
1098 + 1 + SLEN(MSG_KEY) + 1 + 1);
1099
1100 /* Check frame type */
1101 if (frame[idx++] != SPOE_FRM_T_AGENT_DISCON)
1102 return 0;
1103
1104 if (size < min_size) {
1105 spoe_status_code = SPOE_FRM_ERR_INVALID;
1106 return -1;
1107 }
1108
1109 /* Skip flags: fragmentation is not supported for now */
1110 idx += 4;
1111
1112 /* stream-id and frame-id must be cleared */
1113 if (frame[idx] != 0 || frame[idx+1] != 0) {
1114 spoe_status_code = SPOE_FRM_ERR_INVALID;
1115 return -1;
1116 }
1117 idx += 2;
1118
1119 /* There are 2 mandatory items: "status-code" and "message" */
1120
1121 /* Loop on K/V items */
1122 while (idx < size) {
1123 char *str;
1124 uint64_t sz;
1125
1126 /* Decode the item key */
1127 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1128 if (str == NULL) {
1129 spoe_status_code = SPOE_FRM_ERR_INVALID;
1130 return -1;
1131 }
1132
1133 /* Check "status-code" K/V item */
1134 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
1135 int type;
1136
1137 /* The value must be an integer */
1138 type = frame[idx++];
1139 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1140 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1141 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1142 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1143 spoe_status_code = SPOE_FRM_ERR_INVALID;
1144 return -1;
1145 }
1146 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1147 spoe_status_code = SPOE_FRM_ERR_INVALID;
1148 return -1;
1149 }
1150 idx += i;
1151 spoe_status_code = sz;
1152 }
1153
1154 /* Check "message" K/V item */
1155 else if (sz && !memcmp(str, MSG_KEY, sz)) {
1156 /* The value must be a string */
1157 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1158 spoe_status_code = SPOE_FRM_ERR_INVALID;
1159 return -1;
1160 }
1161 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1162 if (str == NULL || sz > 255) {
1163 spoe_status_code = SPOE_FRM_ERR_INVALID;
1164 return -1;
1165 }
1166 memcpy(spoe_reason, str, sz);
1167 spoe_reason[sz] = 0;
1168 }
1169 else {
1170 /* Silently ignore unknown item */
1171 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1172 spoe_status_code = SPOE_FRM_ERR_INVALID;
1173 return -1;
1174 }
1175 idx += i;
1176 }
1177 }
1178
1179 return idx;
1180}
1181
1182
Christopher Fauleta1cda022016-12-21 08:58:06 +01001183/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001184 * success, 0 if the frame can be ignored and -1 if an error occurred. */
1185static int
1186handle_spoe_agentack_frame(struct appctx *appctx, char *frame, size_t size)
1187{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001188 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001189 struct spoe_context *ctx, *back;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001190 uint64_t stream_id, frame_id;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001191 int i, idx = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001192 size_t min_size = (7 /* TYPE + METADATA */);
1193
1194 /* Check frame type */
1195 if (frame[idx++] != SPOE_FRM_T_AGENT_ACK)
1196 return 0;
1197
1198 if (size < min_size) {
1199 spoe_status_code = SPOE_FRM_ERR_INVALID;
1200 return -1;
1201 }
1202
1203 /* Skip flags: fragmentation is not supported for now */
1204 idx += 4;
1205
1206 /* Get the stream-id and the frame-id */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001207 if ((i = decode_spoe_varint(frame+idx, frame+size, &stream_id)) == -1)
1208 return 0;
1209 idx += i;
1210 if ((i= decode_spoe_varint(frame+idx, frame+size, &frame_id)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001211 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001212 idx += i;
1213
Christopher Faulet42bfa462017-01-04 14:14:19 +01001214 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001215 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1216 if (ctx->stream_id == (unsigned int)stream_id &&
1217 ctx->frame_id == (unsigned int)frame_id)
1218 goto found;
1219 }
1220 }
1221 else {
Christopher Faulet42bfa462017-01-04 14:14:19 +01001222 list_for_each_entry_safe(ctx, back, &SPOE_APPCTX(appctx)->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001223 if (ctx->stream_id == (unsigned int)stream_id &&
1224 ctx->frame_id == (unsigned int)frame_id)
1225 goto found;
1226 }
1227 }
1228
1229 /* No Stream found, ignore the frame */
1230 return 0;
1231
1232 found:
Christopher Fauletb067b062017-01-04 16:39:11 +01001233 if (!acquire_spoe_buffer(ctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001234 return 1; /* Retry later */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001235
1236 /* Copy encoded actions */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001237 memcpy(ctx->buffer->p, frame+idx, size-idx);
1238 ctx->buffer->i = size-idx;
1239
Christopher Fauleta1cda022016-12-21 08:58:06 +01001240 /* Notify the stream */
1241 LIST_DEL(&ctx->list);
1242 LIST_INIT(&ctx->list);
1243 ctx->state = SPOE_CTX_ST_DONE;
1244 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1245
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001246 return idx;
1247}
1248
Christopher Fauletba7bc162016-11-07 21:07:38 +01001249/* This function is used in cfgparse.c and declared in proto/checks.h. It
1250 * prepare the request to send to agents during a healthcheck. It returns 0 on
1251 * success and -1 if an error occurred. */
1252int
1253prepare_spoe_healthcheck_request(char **req, int *len)
1254{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001255 struct appctx appctx;
1256 struct spoe_appctx spoe_appctx;
1257 char *frame, buf[global.tune.bufsize];
1258 unsigned int framesz;
1259 int idx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001260
Christopher Faulet42bfa462017-01-04 14:14:19 +01001261 memset(&appctx, 0, sizeof(appctx));
1262 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001263 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001264
1265 appctx.ctx.spoe.ptr = &spoe_appctx;
1266 SPOE_APPCTX(&appctx)->max_frame_size = global.tune.bufsize-4;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001267
1268 frame = buf+4;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001269 idx = prepare_spoe_hahello_frame(&appctx, frame, global.tune.bufsize-4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001270 if (idx <= 0)
1271 return -1;
1272 if (idx + SLEN(HEALTHCHECK_KEY) + 1 > global.tune.bufsize-4)
1273 return -1;
1274
1275 /* "healthcheck" K/V item */
1276 idx += encode_spoe_string(HEALTHCHECK_KEY, SLEN(HEALTHCHECK_KEY), frame+idx);
1277 frame[idx++] = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
1278
1279 framesz = htonl(idx);
1280 memcpy(buf, (char *)&framesz, 4);
1281
1282 if ((*req = malloc(idx+4)) == NULL)
1283 return -1;
1284 memcpy(*req, buf, idx+4);
1285 *len = idx+4;
1286 return 0;
1287}
1288
1289/* This function is used in checks.c and declared in proto/checks.h. It decode
1290 * the response received from an agent during a healthcheck. It returns 0 on
1291 * success and -1 if an error occurred. */
1292int
1293handle_spoe_healthcheck_response(char *frame, size_t size, char *err, int errlen)
1294{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001295 struct appctx appctx;
1296 struct spoe_appctx spoe_appctx;
1297 int r;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001298
Christopher Faulet42bfa462017-01-04 14:14:19 +01001299 memset(&appctx, 0, sizeof(appctx));
1300 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001301
Christopher Faulet42bfa462017-01-04 14:14:19 +01001302 appctx.ctx.spoe.ptr = &spoe_appctx;
1303 SPOE_APPCTX(&appctx)->max_frame_size = global.tune.bufsize-4;
1304
1305 if (handle_spoe_agentdiscon_frame(&appctx, frame, size) != 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001306 goto error;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001307 if ((r = handle_spoe_agenthello_frame(&appctx, frame, size)) <= 0) {
Christopher Fauletba7bc162016-11-07 21:07:38 +01001308 if (r == 0)
1309 spoe_status_code = SPOE_FRM_ERR_INVALID;
1310 goto error;
1311 }
1312
1313 return 0;
1314
1315 error:
1316 if (spoe_status_code >= SPOE_FRM_ERRS)
1317 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
1318 strncpy(err, spoe_frm_err_reasons[spoe_status_code], errlen);
1319 return -1;
1320}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001321
Christopher Fauleta1cda022016-12-21 08:58:06 +01001322/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1323 * the frame can be ignored, 1 to retry later, and the frame legnth on
1324 * success. */
1325static int
1326send_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1327{
1328 struct stream_interface *si = appctx->owner;
1329 int ret;
1330 uint32_t netint;
1331
1332 if (si_ic(si)->buf == &buf_empty)
1333 return 1;
1334
1335 netint = htonl(framesz);
1336 memcpy(buf, (char *)&netint, 4);
1337 ret = bi_putblk(si_ic(si), buf, framesz+4);
1338
1339 if (ret <= 0) {
1340 if (ret == -1)
1341 return 1; /* retry */
Christopher Fauletb067b062017-01-04 16:39:11 +01001342 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001343 return -1; /* error */
1344 }
1345 return framesz;
1346}
1347
1348/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1349 * when the frame can be ignored, 1 to retry later and the frame length on
1350 * success. */
1351static int
1352recv_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1353{
1354 struct stream_interface *si = appctx->owner;
1355 int ret;
1356 uint32_t netint;
1357
1358 if (si_oc(si)->buf == &buf_empty)
1359 return 1;
1360
1361 ret = bo_getblk(si_oc(si), (char *)&netint, 4, 0);
1362 if (ret > 0) {
1363 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001364 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001365 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
1366 return -1;
1367 }
1368 ret = bo_getblk(si_oc(si), trash.str, framesz, 4);
1369 }
1370 if (ret <= 0) {
1371 if (ret == 0)
1372 return 1; /* retry */
1373 spoe_status_code = SPOE_FRM_ERR_IO;
1374 return -1; /* error */
1375 }
1376 return framesz;
1377}
1378
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001379/********************************************************************
1380 * Functions that manage the SPOE applet
1381 ********************************************************************/
1382/* Callback function that catches applet timeouts. If a timeout occurred, we set
1383 * <appctx->st1> flag and the SPOE applet is woken up. */
1384static struct task *
1385process_spoe_applet(struct task * task)
1386{
1387 struct appctx *appctx = task->context;
1388
1389 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1390 if (tick_is_expired(task->expire, now_ms)) {
1391 task->expire = TICK_ETERNITY;
1392 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
1393 }
1394 si_applet_want_get(appctx->owner);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001395 si_applet_want_put(appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001396 appctx_wakeup(appctx);
1397 return task;
1398}
1399
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001400/* Callback function that releases a SPOE applet. This happens when the
1401 * connection with the agent is closed. */
1402static void
1403release_spoe_applet(struct appctx *appctx)
1404{
1405 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001406 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001407 struct spoe_context *ctx, *back;
1408
1409 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1410 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1411 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001412
Christopher Fauleta1cda022016-12-21 08:58:06 +01001413 agent->applets_act--;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001414 if (!LIST_ISEMPTY(&SPOE_APPCTX(appctx)->list)) {
1415 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1416 LIST_INIT(&SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001417 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001418
1419 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001420 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1421 agent->applets_idle--;
1422
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001423 si_shutw(si);
1424 si_shutr(si);
1425 si_ic(si)->flags |= CF_READ_NULL;
1426 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Fauletb067b062017-01-04 16:39:11 +01001427 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
1428 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001429 }
1430
Christopher Faulet42bfa462017-01-04 14:14:19 +01001431 if (SPOE_APPCTX(appctx)->task) {
1432 task_delete(SPOE_APPCTX(appctx)->task);
1433 task_free(SPOE_APPCTX(appctx)->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001434 }
1435
Christopher Faulet42bfa462017-01-04 14:14:19 +01001436 list_for_each_entry_safe(ctx, back, &SPOE_APPCTX(appctx)->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001437 LIST_DEL(&ctx->list);
1438 LIST_INIT(&ctx->list);
1439 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001440 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001441 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001442 }
1443
Christopher Faulet42bfa462017-01-04 14:14:19 +01001444 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
1445
Christopher Fauleta1cda022016-12-21 08:58:06 +01001446 if (!LIST_ISEMPTY(&agent->applets))
1447 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001448
Christopher Fauleta1cda022016-12-21 08:58:06 +01001449 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1450 LIST_DEL(&ctx->list);
1451 LIST_INIT(&ctx->list);
1452 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001453 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001454 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001455 }
1456
Christopher Fauleta1cda022016-12-21 08:58:06 +01001457 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1458 LIST_DEL(&ctx->list);
1459 LIST_INIT(&ctx->list);
1460 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001461 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001462 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1463 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001464}
1465
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001466static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001467handle_connect_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001468{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001469 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001470 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001471 char *frame = trash.str;
1472 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001473
Christopher Fauleta1cda022016-12-21 08:58:06 +01001474 if (si->state <= SI_ST_CON) {
1475 si_applet_want_put(si);
1476 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1477 goto stop;
1478 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001479 if (si->state != SI_ST_EST) {
1480 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001481 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001482 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001483
Christopher Fauleta1cda022016-12-21 08:58:06 +01001484 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1485 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1486 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001487 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001488 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001489 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001490
Christopher Faulet42bfa462017-01-04 14:14:19 +01001491 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
1492 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001493
Christopher Faulet42bfa462017-01-04 14:14:19 +01001494 ret = prepare_spoe_hahello_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001495 if (ret > 1)
1496 ret = send_spoe_frame(appctx, frame, ret);
1497
1498 switch (ret) {
1499 case -1: /* error */
1500 goto exit;
1501
1502 case 0: /* ignore => an error, cannot be ignored */
1503 goto exit;
1504
1505 case 1: /* retry later */
1506 si_applet_cant_put(si);
1507 goto stop;
1508
1509 default: /* CONNECT frame successfully sent */
1510 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1511 goto next;
1512 }
1513
1514 next:
1515 return 0;
1516 stop:
1517 return 1;
1518 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001519 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001520 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1521 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001522}
1523
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001524static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001525handle_connecting_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001526{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001527 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001528 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001529 char *frame = trash.str;
1530 int ret, framesz = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001531
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001532
Christopher Fauletb067b062017-01-04 16:39:11 +01001533 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1534 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001535 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001536 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001537
Christopher Fauleta1cda022016-12-21 08:58:06 +01001538 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1539 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1540 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001541 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001542 goto exit;
1543 }
1544
Christopher Faulet42bfa462017-01-04 14:14:19 +01001545 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001546 if (ret > 1) {
1547 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1548 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1549 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001550 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001551 framesz = ret;
1552 ret = handle_spoe_agenthello_frame(appctx, frame, framesz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001553 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001554
Christopher Fauleta1cda022016-12-21 08:58:06 +01001555 switch (ret) {
1556 case -1: /* error */
1557 if (framesz)
1558 bo_skip(si_oc(si), framesz+4);
1559 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1560 goto next;
1561
1562 case 0: /* ignore */
1563 if (framesz)
1564 bo_skip(si_oc(si), framesz+4);
1565 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1566 goto next;
1567
1568 case 1: /* retry later */
1569 goto stop;
1570
1571 default:
1572 /* hello handshake is finished, set the idle timeout,
1573 * Add the appctx in the agent cache, decrease the
1574 * number of new applets and wake up waiting streams. */
1575 if (framesz)
1576 bo_skip(si_oc(si), framesz+4);
1577 agent->applets_idle++;
1578 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001579 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1580 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001581 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001582 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001583
Christopher Fauleta1cda022016-12-21 08:58:06 +01001584 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001585 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001586 return 0;
1587 stop:
1588 return 1;
1589 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001590 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001591 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1592 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001593}
1594
Christopher Fauleta1cda022016-12-21 08:58:06 +01001595static int
1596handle_processing_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001597{
1598 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001599 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001600 struct spoe_context *ctx;
1601 char *frame = trash.str;
1602 unsigned int fpa = 0;
1603 int ret, framesz = 0, skip_sending = 0, skip_receiving = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001604
Christopher Fauletb067b062017-01-04 16:39:11 +01001605 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1606 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001607 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001608 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001609
Christopher Fauleta1cda022016-12-21 08:58:06 +01001610 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1611 spoe_status_code = SPOE_FRM_ERR_TOUT;
1612 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1613 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1614 goto next;
1615 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001616
Christopher Fauleta1cda022016-12-21 08:58:06 +01001617 process:
1618 if (fpa > agent->max_fpa || (skip_sending && skip_receiving))
1619 goto stop;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001620
Christopher Fauleta1cda022016-12-21 08:58:06 +01001621 /* Frames must be handled synchronously and a the applet is waiting for
1622 * a ACK frame */
Christopher Faulet42bfa462017-01-04 14:14:19 +01001623 if (!(SPOE_APPCTX(appctx)->flags & (SPOE_APPCTX_FL_ASYNC|SPOE_APPCTX_FL_PIPELINING)) &&
1624 !LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001625 if (skip_receiving)
1626 goto stop;
1627 goto recv_frame;
1628 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001629
Christopher Fauleta1cda022016-12-21 08:58:06 +01001630 if (LIST_ISEMPTY(&agent->sending_queue) || skip_sending) {
1631 skip_sending = 1;
1632 goto recv_frame;
1633 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001634
Christopher Fauleta1cda022016-12-21 08:58:06 +01001635 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001636 ret = prepare_spoe_hanotify_frame(appctx, ctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001637 if (ret > 1)
1638 ret = send_spoe_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001639
Christopher Fauleta1cda022016-12-21 08:58:06 +01001640 switch (ret) {
1641 case -1: /* error */
1642 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1643 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001644
Christopher Fauleta1cda022016-12-21 08:58:06 +01001645 case 0: /* ignore */
1646 agent->sending_rate++;
1647 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001648 ctx->status_code = (spoe_status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001649 release_spoe_buffer(ctx);
1650 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1651 LIST_DEL(&ctx->list);
1652 LIST_INIT(&ctx->list);
1653 fpa++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001654 break;
1655
Christopher Fauleta1cda022016-12-21 08:58:06 +01001656 case 1: /* retry */
1657 si_applet_cant_put(si);
1658 skip_sending = 1;
1659 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001660
Christopher Fauleta1cda022016-12-21 08:58:06 +01001661 default:
1662 agent->sending_rate++;
1663 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1664 release_spoe_buffer(ctx);
1665 LIST_DEL(&ctx->list);
1666 LIST_INIT(&ctx->list);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001667 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001668 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1669 else
Christopher Faulet42bfa462017-01-04 14:14:19 +01001670 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001671 fpa++;
1672 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001673
Christopher Fauleta1cda022016-12-21 08:58:06 +01001674 if (fpa > agent->max_fpa)
1675 goto stop;
1676
1677 recv_frame:
1678 if (skip_receiving)
1679 goto process;
1680
1681 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001682 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001683 if (ret > 1) {
1684 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1685 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1686 goto next;
1687 }
1688 framesz = ret;
1689 ret = handle_spoe_agentack_frame(appctx, frame, framesz);
1690 }
1691
1692 switch (ret) {
1693 case -1: /* error */
1694 if (framesz)
1695 bo_skip(si_oc(si), framesz+4);
1696 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1697 goto next;
1698
1699 case 0: /* ignore */
1700 if (framesz)
1701 bo_skip(si_oc(si), framesz+4);
1702 fpa++;
1703 break;
1704
1705 case 1: /* retry */
1706 skip_receiving = 1;
1707 break;
1708
1709 default:
1710 if (framesz)
1711 bo_skip(si_oc(si), framesz+4);
1712 fpa++;
1713 }
1714 goto process;
1715
1716 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001717 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001718 return 0;
1719 stop:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001720 if ((SPOE_APPCTX(appctx)->flags & (SPOE_APPCTX_FL_ASYNC|SPOE_APPCTX_FL_PIPELINING)) ||
1721 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001722 agent->applets_idle++;
1723 appctx->st0 = SPOE_APPCTX_ST_IDLE;
1724 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01001725 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
1726 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1727 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001728 if (fpa)
Christopher Faulet42bfa462017-01-04 14:14:19 +01001729 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001730 }
1731 return 1;
1732
1733 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001734 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001735 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1736 return 0;
1737}
1738
1739static int
1740handle_disconnect_spoe_applet(struct appctx *appctx)
1741{
1742 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001743 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001744 char *frame = trash.str;
1745 int ret;
1746
Christopher Fauletb067b062017-01-04 16:39:11 +01001747 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
1748
Christopher Fauleta1cda022016-12-21 08:58:06 +01001749 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
1750 goto exit;
1751
1752 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
1753 goto exit;
1754
Christopher Faulet42bfa462017-01-04 14:14:19 +01001755 ret = prepare_spoe_hadiscon_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001756 if (ret > 1)
1757 ret = send_spoe_frame(appctx, frame, ret);
1758
1759 switch (ret) {
1760 case -1: /* error */
1761 goto exit;
1762
1763 case 0: /* ignore */
1764 goto exit;
1765
1766 case 1: /* retry */
1767 si_applet_cant_put(si);
1768 goto stop;
1769
1770 default:
1771 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1772 " - disconnected by HAProxy (%d): %s\n",
1773 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1774 __FUNCTION__, appctx, spoe_status_code,
1775 spoe_frm_err_reasons[spoe_status_code]);
1776
1777 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1778 goto next;
1779 }
1780
1781 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001782 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001783 return 0;
1784 stop:
1785 return 1;
1786 exit:
1787 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1788 return 0;
1789}
1790
1791static int
1792handle_disconnecting_spoe_applet(struct appctx *appctx)
1793{
1794 struct stream_interface *si = appctx->owner;
1795 char *frame = trash.str;
1796 int ret, framesz = 0;
1797
Christopher Fauletb067b062017-01-04 16:39:11 +01001798 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1799 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001800 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001801 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001802
Christopher Fauletb067b062017-01-04 16:39:11 +01001803 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1804 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001805 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001806 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001807
1808 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001809 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001810 if (ret > 1) {
1811 framesz = ret;
1812 ret = handle_spoe_agentdiscon_frame(appctx, frame, framesz);
1813 }
1814
1815 switch (ret) {
1816 case -1: /* error */
1817 if (framesz)
1818 bo_skip(si_oc(si), framesz+4);
1819 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1820 " - error on frame (%s)\n",
1821 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001822 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001823 __FUNCTION__, appctx,
1824 spoe_frm_err_reasons[spoe_status_code]);
1825 goto exit;
1826
1827 case 0: /* ignore */
1828 if (framesz)
1829 bo_skip(si_oc(si), framesz+4);
1830 goto next;
1831
1832 case 1: /* retry */
1833 goto stop;
1834
1835 default:
1836 if (framesz)
1837 bo_skip(si_oc(si), framesz+4);
1838 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1839 " - disconnected by peer (%d): %s\n",
1840 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001841 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001842 __FUNCTION__, appctx, spoe_status_code,
1843 spoe_reason);
1844 goto exit;
1845 }
1846
1847 next:
1848 return 0;
1849 stop:
1850 return 1;
1851 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001852 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
1853 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001854 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1855 return 0;
1856}
1857
1858/* I/O Handler processing messages exchanged with the agent */
1859static void
1860handle_spoe_applet(struct appctx *appctx)
1861{
1862 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001863 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001864
Christopher Fauletb067b062017-01-04 16:39:11 +01001865 spoe_status_code = SPOE_FRM_ERR_NONE;
1866
Christopher Fauleta1cda022016-12-21 08:58:06 +01001867 switchstate:
1868 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1869 " - appctx-state=%s\n",
1870 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1871 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
1872
1873 switch (appctx->st0) {
1874 case SPOE_APPCTX_ST_CONNECT:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001875 if (handle_connect_spoe_applet(appctx))
1876 goto out;
1877 goto switchstate;
1878
1879 case SPOE_APPCTX_ST_CONNECTING:
1880 if (handle_connecting_spoe_applet(appctx))
1881 goto out;
1882 goto switchstate;
1883
1884 case SPOE_APPCTX_ST_IDLE:
1885 if (stopping &&
1886 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01001887 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
1888 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001889 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001890 goto switchstate;
1891 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001892 agent->applets_idle--;
1893 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1894 /* fall through */
1895
1896 case SPOE_APPCTX_ST_PROCESSING:
1897 if (handle_processing_spoe_applet(appctx))
1898 goto out;
1899 goto switchstate;
1900
1901 case SPOE_APPCTX_ST_DISCONNECT:
1902 if (handle_disconnect_spoe_applet(appctx))
1903 goto out;
1904 goto switchstate;
1905
1906 case SPOE_APPCTX_ST_DISCONNECTING:
1907 if (handle_disconnecting_spoe_applet(appctx))
1908 goto out;
1909 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001910
1911 case SPOE_APPCTX_ST_EXIT:
1912 si_shutw(si);
1913 si_shutr(si);
1914 si_ic(si)->flags |= CF_READ_NULL;
1915 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001916 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001917 /* fall through */
1918
1919 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001920 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001921 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001922 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001923 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
1924 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001925 si_oc(si)->flags |= CF_READ_DONTWAIT;
1926 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001927}
1928
1929struct applet spoe_applet = {
1930 .obj_type = OBJ_TYPE_APPLET,
1931 .name = "<SPOE>", /* used for logging */
1932 .fct = handle_spoe_applet,
1933 .release = release_spoe_applet,
1934};
1935
1936/* Create a SPOE applet. On success, the created applet is returned, else
1937 * NULL. */
1938static struct appctx *
1939create_spoe_appctx(struct spoe_config *conf)
1940{
1941 struct appctx *appctx;
1942 struct session *sess;
1943 struct task *task;
1944 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001945
1946 if ((appctx = appctx_new(&spoe_applet)) == NULL)
1947 goto out_error;
1948
Christopher Faulet42bfa462017-01-04 14:14:19 +01001949 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
1950 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001951 goto out_free_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001952
Christopher Faulet42bfa462017-01-04 14:14:19 +01001953 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
1954 if ((SPOE_APPCTX(appctx)->task = task_new()) == NULL)
1955 goto out_free_spoe_appctx;
1956
1957 SPOE_APPCTX(appctx)->owner = appctx;
1958 SPOE_APPCTX(appctx)->task->process = process_spoe_applet;
1959 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
1960 SPOE_APPCTX(appctx)->task->context = appctx;
1961 SPOE_APPCTX(appctx)->agent = conf->agent;
1962 SPOE_APPCTX(appctx)->version = 0;
1963 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
1964 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01001965 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001966
1967 LIST_INIT(&SPOE_APPCTX(appctx)->list);
1968 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001969
Willy Tarreau5820a362016-12-22 15:59:02 +01001970 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001971 if (!sess)
1972 goto out_free_spoe;
1973
1974 if ((task = task_new()) == NULL)
1975 goto out_free_sess;
1976
1977 if ((strm = stream_new(sess, task, &appctx->obj_type)) == NULL)
1978 goto out_free_task;
1979
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001980 stream_set_backend(strm, conf->agent->b.be);
1981
1982 /* applet is waiting for data */
1983 si_applet_cant_get(&strm->si[0]);
1984 appctx_wakeup(appctx);
1985
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001986 strm->do_log = NULL;
1987 strm->res.flags |= CF_READ_DONTWAIT;
1988
1989 conf->agent_fe.feconn++;
1990 jobs++;
1991 totalconn++;
1992
Christopher Faulet42bfa462017-01-04 14:14:19 +01001993 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
1994 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001995 conf->agent->applets_act++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001996 return appctx;
1997
1998 /* Error unrolling */
1999 out_free_task:
2000 task_free(task);
2001 out_free_sess:
2002 session_free(sess);
2003 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002004 task_free(SPOE_APPCTX(appctx)->task);
2005 out_free_spoe_appctx:
2006 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002007 out_free_appctx:
2008 appctx_free(appctx);
2009 out_error:
2010 return NULL;
2011}
2012
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002013static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01002014queue_spoe_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002015{
2016 struct spoe_config *conf = FLT_CONF(ctx->filter);
2017 struct spoe_agent *agent = conf->agent;
2018 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002019 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002020 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002021
Christopher Fauleta1cda022016-12-21 08:58:06 +01002022 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002023
Christopher Fauleta1cda022016-12-21 08:58:06 +01002024 /* Check if we need to create a new SPOE applet or not. */
2025 if (agent->applets_act >= min_applets && agent->applets_idle && agent->sending_rate)
2026 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002027
2028 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002029 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002030 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2031 ctx->strm);
2032
Christopher Fauleta1cda022016-12-21 08:58:06 +01002033 /* Do not try to create a new applet if there is no server up for the
2034 * agent's backend. */
2035 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2036 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2037 " - cannot create SPOE appctx: no server up\n",
2038 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2039 __FUNCTION__, ctx->strm);
2040 goto end;
2041 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002042
Christopher Fauleta1cda022016-12-21 08:58:06 +01002043 /* Do not try to create a new applet if we have reached the maximum of
2044 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002045 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002046 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2047 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2048 " - cannot create SPOE appctx: max CPS reached\n",
2049 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2050 __FUNCTION__, ctx->strm);
2051 goto end;
2052 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002053 }
2054
Christopher Fauleta1cda022016-12-21 08:58:06 +01002055 appctx = create_spoe_appctx(conf);
2056 if (appctx == NULL) {
2057 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2058 " - failed to create SPOE appctx\n",
2059 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2060 __FUNCTION__, ctx->strm);
2061 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002062 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002063 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002064 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002065
Christopher Fauleta1cda022016-12-21 08:58:06 +01002066 /* Increase the per-process number of cumulated connections */
2067 if (agent->cps_max > 0)
2068 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002069
Christopher Fauleta1cda022016-12-21 08:58:06 +01002070 end:
2071 /* The only reason to return an error is when there is no applet */
2072 if (LIST_ISEMPTY(&agent->applets))
2073 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002074
Christopher Fauleta1cda022016-12-21 08:58:06 +01002075 /* Add the SPOE context in the sending queue and update all running
2076 * info */
2077 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2078 if (agent->sending_rate)
2079 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002080
2081 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002082 " - Add stream in sending queue - applets_act=%u - applets_idle=%u"
2083 " - sending_rate=%u\n",
2084 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2085 ctx->strm, agent->applets_act, agent->applets_idle, agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002086
Christopher Fauleta1cda022016-12-21 08:58:06 +01002087 /* Finally try to wakeup the first IDLE applet found and move it at the
2088 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002089 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2090 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002091 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
2092 si_applet_want_get(appctx->owner);
2093 si_applet_want_put(appctx->owner);
2094 appctx_wakeup(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002095 LIST_DEL(&spoe_appctx->list);
2096 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002097 break;
2098 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002099 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002100 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002101}
2102
2103/***************************************************************************
2104 * Functions that process SPOE messages and actions
2105 **************************************************************************/
2106/* Process SPOE messages for a specific event. During the processing, it returns
2107 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2108 * is returned. */
2109static int
2110process_spoe_messages(struct stream *s, struct spoe_context *ctx,
2111 struct list *messages, int dir)
2112{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002113 struct spoe_config *conf = FLT_CONF(ctx->filter);
2114 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002115 struct spoe_message *msg;
2116 struct sample *smp;
2117 struct spoe_arg *arg;
2118 char *p;
2119 size_t max_size;
2120 int off, flag, idx = 0;
2121
2122 /* Reserve 32 bytes from the frame Metadata */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002123 max_size = agent->max_frame_size - 32;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002124
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002125 p = ctx->buffer->p;
2126
2127 /* Loop on messages */
2128 list_for_each_entry(msg, messages, list) {
2129 if (idx + msg->id_len + 1 > max_size)
2130 goto skip;
2131
2132 /* Set the message name */
2133 idx += encode_spoe_string(msg->id, msg->id_len, p+idx);
2134
2135 /* Save offset where to store the number of arguments for this
2136 * message */
2137 off = idx++;
2138 p[off] = 0;
2139
2140 /* Loop on arguments */
2141 list_for_each_entry(arg, &msg->args, list) {
2142 p[off]++; /* Increment the number of arguments */
2143
2144 if (idx + arg->name_len + 1 > max_size)
2145 goto skip;
2146
2147 /* Encode the arguement name as a string. It can by NULL */
2148 idx += encode_spoe_string(arg->name, arg->name_len, p+idx);
2149
2150 /* Fetch the arguement value */
2151 smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
2152 if (!smp) {
2153 /* If no value is available, set it to NULL */
2154 p[idx++] = SPOE_DATA_T_NULL;
2155 continue;
2156 }
2157
2158 /* Else, encode the arguement value */
2159 switch (smp->data.type) {
2160 case SMP_T_BOOL:
2161 flag = ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
2162 p[idx++] = (SPOE_DATA_T_BOOL | flag);
2163 break;
2164 case SMP_T_SINT:
2165 p[idx++] = SPOE_DATA_T_INT64;
2166 if (idx + 8 > max_size)
2167 goto skip;
2168 idx += encode_spoe_varint(smp->data.u.sint, p+idx);
2169 break;
2170 case SMP_T_IPV4:
2171 p[idx++] = SPOE_DATA_T_IPV4;
2172 if (idx + 4 > max_size)
2173 goto skip;
2174 memcpy(p+idx, &smp->data.u.ipv4, 4);
2175 idx += 4;
2176 break;
2177 case SMP_T_IPV6:
2178 p[idx++] = SPOE_DATA_T_IPV6;
2179 if (idx + 16 > max_size)
2180 goto skip;
2181 memcpy(p+idx, &smp->data.u.ipv6, 16);
2182 idx += 16;
2183 break;
2184 case SMP_T_STR:
2185 p[idx++] = SPOE_DATA_T_STR;
2186 if (idx + smp->data.u.str.len > max_size)
2187 goto skip;
2188 idx += encode_spoe_string(smp->data.u.str.str,
2189 smp->data.u.str.len,
2190 p+idx);
2191 break;
2192 case SMP_T_BIN:
2193 p[idx++] = SPOE_DATA_T_BIN;
2194 if (idx + smp->data.u.str.len > max_size)
2195 goto skip;
2196 idx += encode_spoe_string(smp->data.u.str.str,
2197 smp->data.u.str.len,
2198 p+idx);
2199 break;
2200 case SMP_T_METH:
2201 if (smp->data.u.meth.meth == HTTP_METH_OTHER) {
2202 p[idx++] = SPOE_DATA_T_STR;
2203 if (idx + http_known_methods[smp->data.u.meth.meth].len > max_size)
2204 goto skip;
2205 idx += encode_spoe_string(http_known_methods[smp->data.u.meth.meth].name,
2206 http_known_methods[smp->data.u.meth.meth].len,
2207 p+idx);
2208 }
2209 else {
2210 p[idx++] = SPOE_DATA_T_STR;
2211 if (idx + smp->data.u.str.len > max_size)
2212 goto skip;
2213 idx += encode_spoe_string(smp->data.u.meth.str.str,
2214 smp->data.u.meth.str.len,
2215 p+idx);
2216 }
2217 break;
2218 default:
2219 p[idx++] = SPOE_DATA_T_NULL;
2220 }
2221 }
2222 }
2223 ctx->buffer->i = idx;
2224 return 1;
2225
2226 skip:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002227 return 0;
2228}
2229
2230/* Helper function to set a variable */
2231static void
2232set_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2233 struct sample *smp)
2234{
2235 struct spoe_config *conf = FLT_CONF(ctx->filter);
2236 struct spoe_agent *agent = conf->agent;
2237 char varname[64];
2238
2239 memset(varname, 0, sizeof(varname));
2240 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2241 scope, agent->var_pfx, len, name);
2242 vars_set_by_name_ifexist(varname, len, smp);
2243}
2244
2245/* Helper function to unset a variable */
2246static void
2247unset_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2248 struct sample *smp)
2249{
2250 struct spoe_config *conf = FLT_CONF(ctx->filter);
2251 struct spoe_agent *agent = conf->agent;
2252 char varname[64];
2253
2254 memset(varname, 0, sizeof(varname));
2255 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2256 scope, agent->var_pfx, len, name);
2257 vars_unset_by_name_ifexist(varname, len, smp);
2258}
2259
2260
2261/* Process SPOE actions for a specific event. During the processing, it returns
2262 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2263 * is returned. */
2264static int
2265process_spoe_actions(struct stream *s, struct spoe_context *ctx,
2266 enum spoe_event ev, int dir)
2267{
2268 char *p;
2269 size_t size;
2270 int off, i, idx = 0;
2271
2272 p = ctx->buffer->p;
2273 size = ctx->buffer->i;
2274
2275 while (idx < size) {
2276 char *str;
2277 uint64_t sz;
2278 struct sample smp;
2279 enum spoe_action_type type;
2280
2281 off = idx;
2282 if (idx+2 > size)
2283 goto skip;
2284
2285 type = p[idx++];
2286 switch (type) {
2287 case SPOE_ACT_T_SET_VAR: {
2288 char *scope;
2289
2290 if (p[idx++] != 3)
2291 goto skip_action;
2292
2293 switch (p[idx++]) {
2294 case SPOE_SCOPE_PROC: scope = "proc"; break;
2295 case SPOE_SCOPE_SESS: scope = "sess"; break;
2296 case SPOE_SCOPE_TXN : scope = "txn"; break;
2297 case SPOE_SCOPE_REQ : scope = "req"; break;
2298 case SPOE_SCOPE_RES : scope = "res"; break;
2299 default: goto skip;
2300 }
2301
2302 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2303 if (str == NULL)
2304 goto skip;
2305 memset(&smp, 0, sizeof(smp));
2306 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002307
2308 if ((i = decode_spoe_data(p+idx, p+size, &smp)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002309 goto skip;
Christopher Fauletb5cff602016-11-24 14:53:22 +01002310 idx += i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002311
2312 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2313 " - set-var '%s.%s.%.*s'\n",
2314 (int)now.tv_sec, (int)now.tv_usec,
2315 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2316 __FUNCTION__, s, scope,
2317 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2318 (int)sz, str);
2319
2320 set_spoe_var(ctx, scope, str, sz, &smp);
2321 break;
2322 }
2323
2324 case SPOE_ACT_T_UNSET_VAR: {
2325 char *scope;
2326
2327 if (p[idx++] != 2)
2328 goto skip_action;
2329
2330 switch (p[idx++]) {
2331 case SPOE_SCOPE_PROC: scope = "proc"; break;
2332 case SPOE_SCOPE_SESS: scope = "sess"; break;
2333 case SPOE_SCOPE_TXN : scope = "txn"; break;
2334 case SPOE_SCOPE_REQ : scope = "req"; break;
2335 case SPOE_SCOPE_RES : scope = "res"; break;
2336 default: goto skip;
2337 }
2338
2339 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2340 if (str == NULL)
2341 goto skip;
2342 memset(&smp, 0, sizeof(smp));
2343 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2344
2345 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2346 " - unset-var '%s.%s.%.*s'\n",
2347 (int)now.tv_sec, (int)now.tv_usec,
2348 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2349 __FUNCTION__, s, scope,
2350 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2351 (int)sz, str);
2352
2353 unset_spoe_var(ctx, scope, str, sz, &smp);
2354 break;
2355 }
2356
2357 default:
2358 skip_action:
2359 if ((i = skip_spoe_action(p+off, p+size)) == -1)
2360 goto skip;
2361 idx += i;
2362 }
2363 }
2364
2365 return 1;
2366 skip:
2367 return 0;
2368}
2369
Christopher Fauleta1cda022016-12-21 08:58:06 +01002370static int
2371start_event_processing(struct spoe_context *ctx, int dir)
2372{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002373 /* If a process is already started for this SPOE context, retry
2374 * later. */
2375 if (ctx->flags & SPOE_CTX_FL_PROCESS)
2376 goto wait;
2377
Christopher Fauletb067b062017-01-04 16:39:11 +01002378 if (!acquire_spoe_buffer(ctx))
2379 goto wait;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002380
2381 /* Set the right flag to prevent request and response processing
2382 * in same time. */
2383 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2384 ? SPOE_CTX_FL_REQ_PROCESS
2385 : SPOE_CTX_FL_RSP_PROCESS);
2386
2387 return 1;
2388
2389 wait:
2390 return 0;
2391}
2392
2393static void
2394stop_event_processing(struct spoe_context *ctx)
2395{
2396 /* Reset the flag to allow next processing */
2397 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
2398
Christopher Fauletb067b062017-01-04 16:39:11 +01002399 ctx->status_code = 0;
2400
Christopher Fauleta1cda022016-12-21 08:58:06 +01002401 /* Reset processing timer */
2402 ctx->process_exp = TICK_ETERNITY;
2403
2404 release_spoe_buffer(ctx);
2405
2406 if (!LIST_ISEMPTY(&ctx->list)) {
2407 LIST_DEL(&ctx->list);
2408 LIST_INIT(&ctx->list);
2409 }
2410}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002411
2412/* Process a SPOE event. First, this functions will process messages attached to
2413 * this event and send them to an agent in a NOTIFY frame. Then, it will wait a
2414 * ACK frame to process corresponding actions. During all the processing, it
2415 * returns 0 and it returns 1 when the processing is finished. If an error
2416 * occurred, -1 is returned. */
2417static int
2418process_spoe_event(struct stream *s, struct spoe_context *ctx,
2419 enum spoe_event ev)
2420{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002421 struct spoe_config *conf = FLT_CONF(ctx->filter);
2422 struct spoe_agent *agent = conf->agent;
2423 int dir, ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002424
2425 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2426 " - ctx-state=%s - event=%s\n",
2427 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletf7a30922016-11-10 15:04:51 +01002428 agent->id, __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002429 spoe_event_str[ev]);
2430
Christopher Faulet48026722016-11-16 15:01:12 +01002431
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002432 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2433
2434 if (LIST_ISEMPTY(&(ctx->messages[ev])))
2435 goto out;
2436
2437 if (ctx->state == SPOE_CTX_ST_ERROR)
2438 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002439
2440 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2441 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2442 " - failed to process event '%s': timeout\n",
2443 (int)now.tv_sec, (int)now.tv_usec,
2444 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
2445 send_log(ctx->strm->be, LOG_WARNING,
2446 "failed to process event '%s': timeout.\n",
2447 spoe_event_str[ev]);
Christopher Fauletb067b062017-01-04 16:39:11 +01002448 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002449 goto error;
2450 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002451
2452 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002453 if (agent->eps_max > 0) {
2454 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
2455 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2456 " - skip event '%s': max EPS reached\n",
2457 (int)now.tv_sec, (int)now.tv_usec,
2458 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
2459 goto skip;
2460 }
2461 }
2462
Christopher Fauletf7a30922016-11-10 15:04:51 +01002463 if (!tick_isset(ctx->process_exp)) {
2464 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2465 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2466 ctx->process_exp);
2467 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002468 ret = start_event_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002469 if (!ret)
2470 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002471 ret = process_spoe_messages(s, ctx, &(ctx->messages[ev]), dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002472 if (!ret)
2473 goto skip;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002474
Christopher Fauletb067b062017-01-04 16:39:11 +01002475 if (!queue_spoe_context(ctx)) {
2476 ctx->status_code = SPOE_CTX_ERR_RES;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002477 goto error;
Christopher Fauletb067b062017-01-04 16:39:11 +01002478 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002479
2480 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2481 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002482 }
2483
Christopher Fauleta1cda022016-12-21 08:58:06 +01002484 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS ||
2485 ctx->state == SPOE_CTX_ST_WAITING_ACK) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002486 ret = 0;
2487 goto out;
2488 }
2489
2490 if (ctx->state == SPOE_CTX_ST_DONE) {
2491 ret = process_spoe_actions(s, ctx, ev, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002492 if (!ret)
2493 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002494 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002495 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002496 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002497 }
2498
2499 out:
2500 return ret;
2501
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002502 error:
Christopher Faulet48026722016-11-16 15:01:12 +01002503 if (agent->eps_max > 0)
2504 update_freq_ctr(&agent->err_per_sec, 1);
2505
Christopher Faulet985532d2016-11-16 15:36:19 +01002506 if (agent->var_on_error) {
2507 struct sample smp;
2508
Christopher Fauleta1cda022016-12-21 08:58:06 +01002509 // FIXME: Get the error code here
Christopher Faulet985532d2016-11-16 15:36:19 +01002510 memset(&smp, 0, sizeof(smp));
2511 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb067b062017-01-04 16:39:11 +01002512 smp.data.u.sint = ctx->status_code;
Christopher Faulet985532d2016-11-16 15:36:19 +01002513 smp.data.type = SMP_T_BOOL;
2514
2515 set_spoe_var(ctx, "txn", agent->var_on_error,
2516 strlen(agent->var_on_error), &smp);
2517 }
2518
Christopher Fauletea62c2a2016-11-14 10:54:21 +01002519 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2520 ? SPOE_CTX_ST_READY
Christopher Fauletb067b062017-01-04 16:39:11 +01002521 : SPOE_CTX_ST_NONE);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002522 ret = 1;
2523 goto end;
2524
2525 skip:
2526 ctx->state = SPOE_CTX_ST_READY;
2527 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002528
Christopher Fauleta1cda022016-12-21 08:58:06 +01002529 end:
2530 stop_event_processing(ctx);
2531 return ret;
2532}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002533
2534/***************************************************************************
2535 * Functions that create/destroy SPOE contexts
2536 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002537static int
2538acquire_spoe_buffer(struct spoe_context *ctx)
2539{
2540 if (ctx->buffer != &buf_empty)
2541 return 1;
2542
2543 if (!LIST_ISEMPTY(&ctx->buffer_wait.list)) {
2544 LIST_DEL(&ctx->buffer_wait.list);
2545 LIST_INIT(&ctx->buffer_wait.list);
2546 }
2547
2548 if (b_alloc_margin(&ctx->buffer, global.tune.reserved_bufs))
2549 return 1;
2550
2551 LIST_ADDQ(&buffer_wq, &ctx->buffer_wait.list);
2552 return 0;
2553}
2554
2555static void
2556release_spoe_buffer(struct spoe_context *ctx)
2557{
2558 if (!LIST_ISEMPTY(&ctx->buffer_wait.list)) {
2559 LIST_DEL(&ctx->buffer_wait.list);
2560 LIST_INIT(&ctx->buffer_wait.list);
2561 }
2562
2563 /* Release the buffer if needed */
2564 if (ctx->buffer != &buf_empty) {
2565 b_free(&ctx->buffer);
2566 offer_buffers(ctx, tasks_run_queue + applets_active_queue);
2567 }
2568}
2569
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002570static int wakeup_spoe_context(struct spoe_context *ctx)
2571{
2572 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2573 return 1;
2574}
2575
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002576static struct spoe_context *
2577create_spoe_context(struct filter *filter)
2578{
2579 struct spoe_config *conf = FLT_CONF(filter);
2580 struct spoe_context *ctx;
2581
2582 ctx = pool_alloc_dirty(pool2_spoe_ctx);
2583 if (ctx == NULL) {
2584 return NULL;
2585 }
2586 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01002587 ctx->filter = filter;
2588 ctx->state = SPOE_CTX_ST_NONE;
2589 ctx->status_code = SPOE_CTX_ERR_NONE;
2590 ctx->flags = 0;
2591 ctx->messages = conf->agent->messages;
2592 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002593 LIST_INIT(&ctx->buffer_wait.list);
2594 ctx->buffer_wait.target = ctx;
2595 ctx->buffer_wait.wakeup_cb = (int (*)(void *))wakeup_spoe_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002596 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002597
Christopher Fauletf7a30922016-11-10 15:04:51 +01002598 ctx->stream_id = 0;
2599 ctx->frame_id = 1;
2600 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002601
2602 return ctx;
2603}
2604
2605static void
2606destroy_spoe_context(struct spoe_context *ctx)
2607{
2608 if (!ctx)
2609 return;
2610
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002611 if (!LIST_ISEMPTY(&ctx->buffer_wait.list))
2612 LIST_DEL(&ctx->buffer_wait.list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002613 if (!LIST_ISEMPTY(&ctx->list))
2614 LIST_DEL(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002615 pool_free2(pool2_spoe_ctx, ctx);
2616}
2617
2618static void
2619reset_spoe_context(struct spoe_context *ctx)
2620{
2621 ctx->state = SPOE_CTX_ST_READY;
2622 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
2623}
2624
2625
2626/***************************************************************************
2627 * Hooks that manage the filter lifecycle (init/check/deinit)
2628 **************************************************************************/
2629/* Signal handler: Do a soft stop, wakeup SPOE applet */
2630static void
2631sig_stop_spoe(struct sig_handler *sh)
2632{
2633 struct proxy *p;
2634
2635 p = proxy;
2636 while (p) {
2637 struct flt_conf *fconf;
2638
2639 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01002640 struct spoe_config *conf;
2641 struct spoe_agent *agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002642 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002643 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002644
Christopher Faulet3b386a32017-02-23 10:17:15 +01002645 if (fconf->id != spoe_filter_id)
2646 continue;
2647
2648 conf = fconf->conf;
2649 agent = conf->agent;
2650
Christopher Faulet42bfa462017-01-04 14:14:19 +01002651 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2652 appctx = spoe_appctx->owner;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002653 si_applet_want_get(appctx->owner);
2654 si_applet_want_put(appctx->owner);
2655 appctx_wakeup(appctx);
2656 }
2657 }
2658 p = p->next;
2659 }
2660}
2661
2662
2663/* Initialize the SPOE filter. Returns -1 on error, else 0. */
2664static int
2665spoe_init(struct proxy *px, struct flt_conf *fconf)
2666{
2667 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002668
2669 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
2670 init_new_proxy(&conf->agent_fe);
2671 conf->agent_fe.parent = conf->agent;
2672 conf->agent_fe.last_change = now.tv_sec;
2673 conf->agent_fe.id = conf->agent->id;
2674 conf->agent_fe.cap = PR_CAP_FE;
2675 conf->agent_fe.mode = PR_MODE_TCP;
2676 conf->agent_fe.maxconn = 0;
2677 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
2678 conf->agent_fe.conn_retries = CONN_RETRIES;
2679 conf->agent_fe.accept = frontend_accept;
2680 conf->agent_fe.srv = NULL;
2681 conf->agent_fe.timeout.client = TICK_ETERNITY;
2682 conf->agent_fe.default_target = &spoe_applet.obj_type;
2683 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
2684
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002685 if (!sighandler_registered) {
2686 signal_register_fct(0, sig_stop_spoe, 0);
2687 sighandler_registered = 1;
2688 }
2689
2690 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002691}
2692
2693/* Free ressources allocated by the SPOE filter. */
2694static void
2695spoe_deinit(struct proxy *px, struct flt_conf *fconf)
2696{
2697 struct spoe_config *conf = fconf->conf;
2698
2699 if (conf) {
2700 struct spoe_agent *agent = conf->agent;
2701 struct listener *l = LIST_NEXT(&conf->agent_fe.conf.listeners,
2702 struct listener *, by_fe);
2703
2704 free(l);
2705 release_spoe_agent(agent);
2706 free(conf);
2707 }
2708 fconf->conf = NULL;
2709}
2710
2711/* Check configuration of a SPOE filter for a specified proxy.
2712 * Return 1 on error, else 0. */
2713static int
2714spoe_check(struct proxy *px, struct flt_conf *fconf)
2715{
2716 struct spoe_config *conf = fconf->conf;
2717 struct proxy *target;
2718
2719 target = proxy_be_by_name(conf->agent->b.name);
2720 if (target == NULL) {
2721 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
2722 " declared at %s:%d.\n",
2723 px->id, conf->agent->b.name, conf->agent->id,
2724 conf->agent->conf.file, conf->agent->conf.line);
2725 return 1;
2726 }
2727 if (target->mode != PR_MODE_TCP) {
2728 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
2729 " at %s:%d does not support HTTP mode.\n",
2730 px->id, target->id, conf->agent->id,
2731 conf->agent->conf.file, conf->agent->conf.line);
2732 return 1;
2733 }
2734
2735 free(conf->agent->b.name);
2736 conf->agent->b.name = NULL;
2737 conf->agent->b.be = target;
2738 return 0;
2739}
2740
2741/**************************************************************************
2742 * Hooks attached to a stream
2743 *************************************************************************/
2744/* Called when a filter instance is created and attach to a stream. It creates
2745 * the context that will be used to process this stream. */
2746static int
2747spoe_start(struct stream *s, struct filter *filter)
2748{
2749 struct spoe_context *ctx;
2750
2751 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
2752 (int)now.tv_sec, (int)now.tv_usec,
2753 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2754 __FUNCTION__, s);
2755
2756 ctx = create_spoe_context(filter);
2757 if (ctx == NULL) {
2758 send_log(s->be, LOG_EMERG,
2759 "failed to create SPOE context for proxy %s\n",
2760 s->be->id);
2761 return 0;
2762 }
2763
2764 ctx->strm = s;
2765 ctx->state = SPOE_CTX_ST_READY;
2766 filter->ctx = ctx;
2767
2768 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_FE]))
2769 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
2770
2771 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_BE]))
2772 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
2773
2774 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_RSP]))
2775 filter->pre_analyzers |= AN_RES_INSPECT;
2776
2777 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_FE]))
2778 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
2779
2780 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_BE]))
2781 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
2782
2783 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_RSP]))
2784 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
2785
2786 return 1;
2787}
2788
2789/* Called when a filter instance is detached from a stream. It release the
2790 * attached SPOE context. */
2791static void
2792spoe_stop(struct stream *s, struct filter *filter)
2793{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002794 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
2795 (int)now.tv_sec, (int)now.tv_usec,
2796 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2797 __FUNCTION__, s);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002798 destroy_spoe_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002799}
2800
Christopher Fauletf7a30922016-11-10 15:04:51 +01002801
2802/*
2803 * Called when the stream is woken up because of expired timer.
2804 */
2805static void
2806spoe_check_timeouts(struct stream *s, struct filter *filter)
2807{
2808 struct spoe_context *ctx = filter->ctx;
2809
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002810 if (tick_is_expired(ctx->process_exp, now_ms)) {
2811 s->pending_events |= TASK_WOKEN_MSG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002812 release_spoe_buffer(ctx);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002813 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01002814}
2815
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002816/* Called when we are ready to filter data on a channel */
2817static int
2818spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
2819{
2820 struct spoe_context *ctx = filter->ctx;
2821 int ret = 1;
2822
2823 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2824 " - ctx-flags=0x%08x\n",
2825 (int)now.tv_sec, (int)now.tv_usec,
2826 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2827 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
2828
Christopher Fauletb067b062017-01-04 16:39:11 +01002829 if (ctx->state == SPOE_CTX_ST_NONE)
2830 goto out;
2831
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002832 if (!(chn->flags & CF_ISRESP)) {
2833 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
2834 chn->analysers |= AN_REQ_INSPECT_FE;
2835 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
2836 chn->analysers |= AN_REQ_INSPECT_BE;
2837
2838 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
2839 goto out;
2840
2841 ctx->stream_id = s->uniq_id;
Christopher Fauletb067b062017-01-04 16:39:11 +01002842 ret = process_spoe_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
2843 if (!ret)
2844 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002845 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
2846 }
2847 else {
2848 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
2849 chn->analysers |= AN_RES_INSPECT;
2850
2851 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
2852 goto out;
2853
Christopher Fauletb067b062017-01-04 16:39:11 +01002854 ret = process_spoe_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002855 if (!ret) {
2856 channel_dont_read(chn);
2857 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01002858 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002859 }
Christopher Fauletb067b062017-01-04 16:39:11 +01002860 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002861 }
2862
2863 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002864 return ret;
2865}
2866
2867/* Called before a processing happens on a given channel */
2868static int
2869spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
2870 struct channel *chn, unsigned an_bit)
2871{
2872 struct spoe_context *ctx = filter->ctx;
2873 int ret = 1;
2874
2875 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2876 " - ctx-flags=0x%08x - ana=0x%08x\n",
2877 (int)now.tv_sec, (int)now.tv_usec,
2878 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2879 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2880 ctx->flags, an_bit);
2881
Christopher Fauletb067b062017-01-04 16:39:11 +01002882 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002883 goto out;
2884
2885 switch (an_bit) {
2886 case AN_REQ_INSPECT_FE:
2887 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
2888 break;
2889 case AN_REQ_INSPECT_BE:
2890 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
2891 break;
2892 case AN_RES_INSPECT:
2893 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_RSP);
2894 break;
2895 case AN_REQ_HTTP_PROCESS_FE:
2896 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
2897 break;
2898 case AN_REQ_HTTP_PROCESS_BE:
2899 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
2900 break;
2901 case AN_RES_HTTP_PROCESS_FE:
2902 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
2903 break;
2904 }
2905
2906 out:
2907 if (!ret) {
2908 channel_dont_read(chn);
2909 channel_dont_close(chn);
2910 }
2911 return ret;
2912}
2913
2914/* Called when the filtering on the channel ends. */
2915static int
2916spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
2917{
2918 struct spoe_context *ctx = filter->ctx;
2919
2920 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2921 " - ctx-flags=0x%08x\n",
2922 (int)now.tv_sec, (int)now.tv_usec,
2923 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2924 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
2925
2926 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
2927 reset_spoe_context(ctx);
2928 }
2929
2930 return 1;
2931}
2932
2933/********************************************************************
2934 * Functions that manage the filter initialization
2935 ********************************************************************/
2936struct flt_ops spoe_ops = {
2937 /* Manage SPOE filter, called for each filter declaration */
2938 .init = spoe_init,
2939 .deinit = spoe_deinit,
2940 .check = spoe_check,
2941
2942 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01002943 .attach = spoe_start,
2944 .detach = spoe_stop,
2945 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002946
2947 /* Handle channels activity */
2948 .channel_start_analyze = spoe_start_analyze,
2949 .channel_pre_analyze = spoe_chn_pre_analyze,
2950 .channel_end_analyze = spoe_end_analyze,
2951};
2952
2953
2954static int
2955cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
2956{
2957 const char *err;
2958 int i, err_code = 0;
2959
2960 if ((cfg_scope == NULL && curengine != NULL) ||
2961 (cfg_scope != NULL && curengine == NULL) ||
2962 strcmp(curengine, cfg_scope))
2963 goto out;
2964
2965 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
2966 if (!*args[1]) {
2967 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
2968 file, linenum);
2969 err_code |= ERR_ALERT | ERR_ABORT;
2970 goto out;
2971 }
2972 if (*args[2]) {
2973 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
2974 file, linenum, args[2]);
2975 err_code |= ERR_ALERT | ERR_ABORT;
2976 goto out;
2977 }
2978
2979 err = invalid_char(args[1]);
2980 if (err) {
2981 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
2982 file, linenum, *err, args[0], args[1]);
2983 err_code |= ERR_ALERT | ERR_ABORT;
2984 goto out;
2985 }
2986
2987 if (curagent != NULL) {
2988 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
2989 file, linenum);
2990 err_code |= ERR_ALERT | ERR_ABORT;
2991 goto out;
2992 }
2993 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
2994 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
2995 err_code |= ERR_ALERT | ERR_ABORT;
2996 goto out;
2997 }
2998
2999 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003000
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003001 curagent->conf.file = strdup(file);
3002 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003003
3004 curagent->timeout.hello = TICK_ETERNITY;
3005 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003006 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003007
3008 curagent->engine_id = NULL;
3009 curagent->var_pfx = NULL;
3010 curagent->var_on_error = NULL;
3011 curagent->flags = 0;
3012 curagent->cps_max = 0;
3013 curagent->eps_max = 0;
3014 curagent->max_frame_size = global.tune.bufsize - 4;
3015 curagent->min_applets = 0;
3016 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003017
3018 for (i = 0; i < SPOE_EV_EVENTS; ++i)
3019 LIST_INIT(&curagent->messages[i]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003020
3021 curagent->applets_act = 0;
3022 curagent->applets_idle = 0;
3023 curagent->sending_rate = 0;
3024
3025 LIST_INIT(&curagent->applets);
3026 LIST_INIT(&curagent->sending_queue);
3027 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003028 }
3029 else if (!strcmp(args[0], "use-backend")) {
3030 if (!*args[1]) {
3031 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3032 file, linenum, args[0]);
3033 err_code |= ERR_ALERT | ERR_FATAL;
3034 goto out;
3035 }
3036 if (*args[2]) {
3037 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3038 file, linenum, args[2]);
3039 err_code |= ERR_ALERT | ERR_ABORT;
3040 goto out;
3041 }
3042 free(curagent->b.name);
3043 curagent->b.name = strdup(args[1]);
3044 }
3045 else if (!strcmp(args[0], "messages")) {
3046 int cur_arg = 1;
3047 while (*args[cur_arg]) {
3048 struct spoe_msg_placeholder *mp = NULL;
3049
3050 list_for_each_entry(mp, &curmps, list) {
3051 if (!strcmp(mp->id, args[cur_arg])) {
3052 Alert("parsing [%s:%d]: spoe-message message '%s' already declared.\n",
3053 file, linenum, args[cur_arg]);
3054 err_code |= ERR_ALERT | ERR_FATAL;
3055 goto out;
3056 }
3057 }
3058
3059 if ((mp = calloc(1, sizeof(*mp))) == NULL) {
3060 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3061 err_code |= ERR_ALERT | ERR_ABORT;
3062 goto out;
3063 }
3064 mp->id = strdup(args[cur_arg]);
3065 LIST_ADDQ(&curmps, &mp->list);
3066 cur_arg++;
3067 }
3068 }
3069 else if (!strcmp(args[0], "timeout")) {
3070 unsigned int *tv = NULL;
3071 const char *res;
3072 unsigned timeout;
3073
3074 if (!*args[1]) {
3075 Alert("parsing [%s:%d] : 'timeout' expects 'connect', 'idle' and 'ack'.\n",
3076 file, linenum);
3077 err_code |= ERR_ALERT | ERR_FATAL;
3078 goto out;
3079 }
3080 if (!strcmp(args[1], "hello"))
3081 tv = &curagent->timeout.hello;
3082 else if (!strcmp(args[1], "idle"))
3083 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003084 else if (!strcmp(args[1], "processing"))
3085 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003086 else {
Christopher Faulet03a34492016-11-19 16:47:56 +01003087 Alert("parsing [%s:%d] : 'timeout' supports 'connect', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003088 file, linenum, args[1]);
3089 err_code |= ERR_ALERT | ERR_FATAL;
3090 goto out;
3091 }
3092 if (!*args[2]) {
3093 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3094 file, linenum, args[1]);
3095 err_code |= ERR_ALERT | ERR_FATAL;
3096 goto out;
3097 }
3098 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3099 if (res) {
3100 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3101 file, linenum, *res, args[1]);
3102 err_code |= ERR_ALERT | ERR_ABORT;
3103 goto out;
3104 }
3105 if (*args[3]) {
3106 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3107 file, linenum, args[3]);
3108 err_code |= ERR_ALERT | ERR_ABORT;
3109 goto out;
3110 }
3111 *tv = MS_TO_TICKS(timeout);
3112 }
3113 else if (!strcmp(args[0], "option")) {
3114 if (!*args[1]) {
3115 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3116 file, linenum, args[0]);
3117 err_code |= ERR_ALERT | ERR_FATAL;
3118 goto out;
3119 }
3120 if (!strcmp(args[1], "var-prefix")) {
3121 char *tmp;
3122
3123 if (!*args[2]) {
3124 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3125 file, linenum, args[0],
3126 args[1]);
3127 err_code |= ERR_ALERT | ERR_FATAL;
3128 goto out;
3129 }
3130 tmp = args[2];
3131 while (*tmp) {
3132 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3133 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3134 file, linenum, args[0], args[1]);
3135 err_code |= ERR_ALERT | ERR_FATAL;
3136 goto out;
3137 }
3138 tmp++;
3139 }
3140 curagent->var_pfx = strdup(args[2]);
3141 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003142 else if (!strcmp(args[1], "continue-on-error")) {
3143 if (*args[2]) {
3144 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
Christopher Faulet48026722016-11-16 15:01:12 +01003145 file, linenum, args[2]);
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003146 err_code |= ERR_ALERT | ERR_ABORT;
3147 goto out;
3148 }
3149 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3150 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003151 else if (!strcmp(args[1], "set-on-error")) {
3152 char *tmp;
3153
3154 if (!*args[2]) {
3155 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3156 file, linenum, args[0],
3157 args[1]);
3158 err_code |= ERR_ALERT | ERR_FATAL;
3159 goto out;
3160 }
3161 tmp = args[2];
3162 while (*tmp) {
3163 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3164 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3165 file, linenum, args[0], args[1]);
3166 err_code |= ERR_ALERT | ERR_FATAL;
3167 goto out;
3168 }
3169 tmp++;
3170 }
3171 curagent->var_on_error = strdup(args[2]);
3172 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003173 else {
3174 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3175 file, linenum, args[1]);
3176 err_code |= ERR_ALERT | ERR_FATAL;
3177 goto out;
3178 }
Christopher Faulet48026722016-11-16 15:01:12 +01003179 }
3180 else if (!strcmp(args[0], "maxconnrate")) {
3181 if (!*args[1]) {
3182 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3183 file, linenum, args[0]);
3184 err_code |= ERR_ALERT | ERR_FATAL;
3185 goto out;
3186 }
3187 if (*args[2]) {
3188 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3189 file, linenum, args[2]);
3190 err_code |= ERR_ALERT | ERR_ABORT;
3191 goto out;
3192 }
3193 curagent->cps_max = atol(args[1]);
3194 }
3195 else if (!strcmp(args[0], "maxerrrate")) {
3196 if (!*args[1]) {
3197 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3198 file, linenum, args[0]);
3199 err_code |= ERR_ALERT | ERR_FATAL;
3200 goto out;
3201 }
3202 if (*args[2]) {
3203 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3204 file, linenum, args[2]);
3205 err_code |= ERR_ALERT | ERR_ABORT;
3206 goto out;
3207 }
3208 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003209 }
3210 else if (*args[0]) {
3211 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3212 file, linenum, args[0]);
3213 err_code |= ERR_ALERT | ERR_FATAL;
3214 goto out;
3215 }
3216 out:
3217 return err_code;
3218}
3219
3220static int
3221cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3222{
3223 struct spoe_message *msg;
3224 struct spoe_arg *arg;
3225 const char *err;
3226 char *errmsg = NULL;
3227 int err_code = 0;
3228
3229 if ((cfg_scope == NULL && curengine != NULL) ||
3230 (cfg_scope != NULL && curengine == NULL) ||
3231 strcmp(curengine, cfg_scope))
3232 goto out;
3233
3234 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3235 if (!*args[1]) {
3236 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3237 file, linenum);
3238 err_code |= ERR_ALERT | ERR_ABORT;
3239 goto out;
3240 }
3241 if (*args[2]) {
3242 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3243 file, linenum, args[2]);
3244 err_code |= ERR_ALERT | ERR_ABORT;
3245 goto out;
3246 }
3247
3248 err = invalid_char(args[1]);
3249 if (err) {
3250 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3251 file, linenum, *err, args[0], args[1]);
3252 err_code |= ERR_ALERT | ERR_ABORT;
3253 goto out;
3254 }
3255
3256 list_for_each_entry(msg, &curmsgs, list) {
3257 if (!strcmp(msg->id, args[1])) {
3258 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3259 " name as another one declared at %s:%d.\n",
3260 file, linenum, args[1], msg->conf.file, msg->conf.line);
3261 err_code |= ERR_ALERT | ERR_FATAL;
3262 goto out;
3263 }
3264 }
3265
3266 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3267 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3268 err_code |= ERR_ALERT | ERR_ABORT;
3269 goto out;
3270 }
3271
3272 curmsg->id = strdup(args[1]);
3273 curmsg->id_len = strlen(curmsg->id);
3274 curmsg->event = SPOE_EV_NONE;
3275 curmsg->conf.file = strdup(file);
3276 curmsg->conf.line = linenum;
3277 LIST_INIT(&curmsg->args);
3278 LIST_ADDQ(&curmsgs, &curmsg->list);
3279 }
3280 else if (!strcmp(args[0], "args")) {
3281 int cur_arg = 1;
3282
3283 curproxy->conf.args.ctx = ARGC_SPOE;
3284 curproxy->conf.args.file = file;
3285 curproxy->conf.args.line = linenum;
3286 while (*args[cur_arg]) {
3287 char *delim = strchr(args[cur_arg], '=');
3288 int idx = 0;
3289
3290 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3291 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3292 err_code |= ERR_ALERT | ERR_ABORT;
3293 goto out;
3294 }
3295
3296 if (!delim) {
3297 arg->name = NULL;
3298 arg->name_len = 0;
3299 delim = args[cur_arg];
3300 }
3301 else {
3302 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3303 arg->name_len = delim - args[cur_arg];
3304 delim++;
3305 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003306 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3307 &idx, file, linenum, &errmsg,
3308 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003309 if (arg->expr == NULL) {
3310 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3311 err_code |= ERR_ALERT | ERR_FATAL;
3312 free(arg->name);
3313 free(arg);
3314 goto out;
3315 }
3316 LIST_ADDQ(&curmsg->args, &arg->list);
3317 cur_arg++;
3318 }
3319 curproxy->conf.args.file = NULL;
3320 curproxy->conf.args.line = 0;
3321 }
3322 else if (!strcmp(args[0], "event")) {
3323 if (!*args[1]) {
3324 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
3325 err_code |= ERR_ALERT | ERR_ABORT;
3326 goto out;
3327 }
3328 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3329 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3330 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3331 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3332
3333 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3334 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3335 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3336 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3337 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3338 curmsg->event = SPOE_EV_ON_TCP_RSP;
3339
3340 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3341 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3342 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3343 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3344 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3345 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3346 else {
3347 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3348 file, linenum, args[1]);
3349 err_code |= ERR_ALERT | ERR_ABORT;
3350 goto out;
3351 }
3352 }
3353 else if (!*args[0]) {
3354 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3355 file, linenum, args[0]);
3356 err_code |= ERR_ALERT | ERR_FATAL;
3357 goto out;
3358 }
3359 out:
3360 free(errmsg);
3361 return err_code;
3362}
3363
3364/* Return -1 on error, else 0 */
3365static int
3366parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
3367 struct flt_conf *fconf, char **err, void *private)
3368{
3369 struct list backup_sections;
3370 struct spoe_config *conf;
3371 struct spoe_message *msg, *msgback;
3372 struct spoe_msg_placeholder *mp, *mpback;
3373 char *file = NULL, *engine = NULL;
3374 int ret, pos = *cur_arg + 1;
3375
3376 conf = calloc(1, sizeof(*conf));
3377 if (conf == NULL) {
3378 memprintf(err, "%s: out of memory", args[*cur_arg]);
3379 goto error;
3380 }
3381 conf->proxy = px;
3382
3383 while (*args[pos]) {
3384 if (!strcmp(args[pos], "config")) {
3385 if (!*args[pos+1]) {
3386 memprintf(err, "'%s' : '%s' option without value",
3387 args[*cur_arg], args[pos]);
3388 goto error;
3389 }
3390 file = args[pos+1];
3391 pos += 2;
3392 }
3393 else if (!strcmp(args[pos], "engine")) {
3394 if (!*args[pos+1]) {
3395 memprintf(err, "'%s' : '%s' option without value",
3396 args[*cur_arg], args[pos]);
3397 goto error;
3398 }
3399 engine = args[pos+1];
3400 pos += 2;
3401 }
3402 else {
3403 memprintf(err, "unknown keyword '%s'", args[pos]);
3404 goto error;
3405 }
3406 }
3407 if (file == NULL) {
3408 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
3409 goto error;
3410 }
3411
3412 /* backup sections and register SPOE sections */
3413 LIST_INIT(&backup_sections);
3414 cfg_backup_sections(&backup_sections);
3415 cfg_register_section("spoe-agent", cfg_parse_spoe_agent);
3416 cfg_register_section("spoe-message", cfg_parse_spoe_message);
3417
3418 /* Parse SPOE filter configuration file */
3419 curengine = engine;
3420 curproxy = px;
3421 curagent = NULL;
3422 curmsg = NULL;
3423 ret = readcfgfile(file);
3424 curproxy = NULL;
3425
3426 /* unregister SPOE sections and restore previous sections */
3427 cfg_unregister_sections();
3428 cfg_restore_sections(&backup_sections);
3429
3430 if (ret == -1) {
3431 memprintf(err, "Could not open configuration file %s : %s",
3432 file, strerror(errno));
3433 goto error;
3434 }
3435 if (ret & (ERR_ABORT|ERR_FATAL)) {
3436 memprintf(err, "Error(s) found in configuration file %s", file);
3437 goto error;
3438 }
3439
3440 /* Check SPOE agent */
3441 if (curagent == NULL) {
3442 memprintf(err, "No SPOE agent found in file %s", file);
3443 goto error;
3444 }
3445 if (curagent->b.name == NULL) {
3446 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
3447 curagent->id, curagent->conf.file, curagent->conf.line);
3448 goto error;
3449 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003450 if (curagent->timeout.hello == TICK_ETERNITY ||
3451 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01003452 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003453 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
3454 " | While not properly invalid, you will certainly encounter various problems\n"
3455 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01003456 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003457 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3458 }
3459 if (curagent->var_pfx == NULL) {
3460 char *tmp = curagent->id;
3461
3462 while (*tmp) {
3463 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3464 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
3465 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
3466 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
3467 goto error;
3468 }
3469 tmp++;
3470 }
3471 curagent->var_pfx = strdup(curagent->id);
3472 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01003473 if (curagent->engine_id == NULL)
3474 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003475
3476 if (LIST_ISEMPTY(&curmps)) {
3477 Warning("Proxy '%s': No message used by SPOE agent '%s' declared at %s:%d.\n",
3478 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3479 goto finish;
3480 }
3481
3482 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3483 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3484 if (!strcmp(msg->id, mp->id)) {
3485 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
3486 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
3487 msg->event = SPOE_EV_ON_TCP_REQ_FE;
3488 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
3489 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
3490 }
3491 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
3492 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
3493 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
3494 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
3495 px->id, msg->conf.file, msg->conf.line);
3496 goto next;
3497 }
3498 if (msg->event == SPOE_EV_NONE) {
3499 Warning("Proxy '%s': Ignore SPOE message without event at %s:%d.\n",
3500 px->id, msg->conf.file, msg->conf.line);
3501 goto next;
3502 }
3503 msg->agent = curagent;
3504 LIST_DEL(&msg->list);
3505 LIST_ADDQ(&curagent->messages[msg->event], &msg->list);
3506 goto next;
3507 }
3508 }
3509 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
3510 curagent->id, mp->id, curagent->conf.file, curagent->conf.line);
3511 goto error;
3512 next:
3513 continue;
3514 }
3515
3516 finish:
3517 conf->agent = curagent;
3518 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3519 LIST_DEL(&mp->list);
3520 release_spoe_msg_placeholder(mp);
3521 }
3522 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3523 Warning("Proxy '%s': Ignore unused SPOE messages '%s' declared at %s:%d.\n",
3524 px->id, msg->id, msg->conf.file, msg->conf.line);
3525 LIST_DEL(&msg->list);
3526 release_spoe_message(msg);
3527 }
3528
3529 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01003530 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003531 fconf->ops = &spoe_ops;
3532 fconf->conf = conf;
3533 return 0;
3534
3535 error:
3536 release_spoe_agent(curagent);
3537 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3538 LIST_DEL(&mp->list);
3539 release_spoe_msg_placeholder(mp);
3540 }
3541 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3542 LIST_DEL(&msg->list);
3543 release_spoe_message(msg);
3544 }
3545 free(conf);
3546 return -1;
3547}
3548
3549
3550/* Declare the filter parser for "spoe" keyword */
3551static struct flt_kw_list flt_kws = { "SPOE", { }, {
3552 { "spoe", parse_spoe_flt, NULL },
3553 { NULL, NULL, NULL },
3554 }
3555};
3556
3557__attribute__((constructor))
3558static void __spoe_init(void)
3559{
3560 flt_register_keywords(&flt_kws);
3561
3562 LIST_INIT(&curmsgs);
3563 LIST_INIT(&curmps);
3564 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003565 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003566}
3567
3568__attribute__((destructor))
3569static void
3570__spoe_deinit(void)
3571{
3572 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003573 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003574}