blob: 2ac3d4b614deff1d51bcbb2893b3efa6a7bf3214 [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 Faulet7aa0b2b2017-01-13 11:30:50 +010052/* Reserved 4 bytes to the frame size. So a frame and its size can be written
53 * together in a buffer */
54#define MAX_FRAME_SIZE global.tune.bufsize - 4
55
56/* The minimum size for a frame */
57#define MIN_FRAME_SIZE 256
58
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010059/* Reserved for the metadata and the frame type.
60 * So <MAX_FRAME_SIZE> - <FRAME_HDR_SIZE> is the maximum payload size */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010061#define FRAME_HDR_SIZE 32
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020062
Christopher Fauletea62c2a2016-11-14 10:54:21 +010063/* Flags set on the SPOE agent */
64#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */
65
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020066/* Flags set on the SPOE context */
67#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
68#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
69#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */
70#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010071#define SPOE_CTX_FL_FRAGMENTED 0x00000010 /* Set when a fragmented frame is processing */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020072
73#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
74
Christopher Fauleta1cda022016-12-21 08:58:06 +010075/* Flags set on the SPOE applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010076#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */
77#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */
78#define SPOE_APPCTX_FL_FRAGMENTATION 0x00000004 /* Set if fragmentation is supported */
79#define SPOE_APPCTX_FL_PERSIST 0x00000008 /* Set if the applet is persistent */
Christopher Fauleta1cda022016-12-21 08:58:06 +010080
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020081#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */
82#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */
83
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010084/* Flags set on the SPOE frame */
85#define SPOE_FRM_FL_FIN 0x00000001
86#define SPOE_FRM_FL_ABRT 0x00000002
87
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020088/* All possible states for a SPOE context */
89enum spoe_ctx_state {
90 SPOE_CTX_ST_NONE = 0,
91 SPOE_CTX_ST_READY,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010092 SPOE_CTX_ST_ENCODING_MSGS,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020093 SPOE_CTX_ST_SENDING_MSGS,
94 SPOE_CTX_ST_WAITING_ACK,
95 SPOE_CTX_ST_DONE,
96 SPOE_CTX_ST_ERROR,
97};
98
99/* All possible states for a SPOE applet */
100enum spoe_appctx_state {
101 SPOE_APPCTX_ST_CONNECT = 0,
102 SPOE_APPCTX_ST_CONNECTING,
Christopher Fauleta1cda022016-12-21 08:58:06 +0100103 SPOE_APPCTX_ST_IDLE,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200104 SPOE_APPCTX_ST_PROCESSING,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100105 SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY,
106 SPOE_APPCTX_ST_WAITING_SYNC_ACK,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200107 SPOE_APPCTX_ST_DISCONNECT,
108 SPOE_APPCTX_ST_DISCONNECTING,
109 SPOE_APPCTX_ST_EXIT,
110 SPOE_APPCTX_ST_END,
111};
112
113/* All supported SPOE actions */
114enum spoe_action_type {
115 SPOE_ACT_T_SET_VAR = 1,
116 SPOE_ACT_T_UNSET_VAR,
117 SPOE_ACT_TYPES,
118};
119
120/* All supported SPOE events */
121enum spoe_event {
122 SPOE_EV_NONE = 0,
123
124 /* Request events */
125 SPOE_EV_ON_CLIENT_SESS = 1,
126 SPOE_EV_ON_TCP_REQ_FE,
127 SPOE_EV_ON_TCP_REQ_BE,
128 SPOE_EV_ON_HTTP_REQ_FE,
129 SPOE_EV_ON_HTTP_REQ_BE,
130
131 /* Response events */
132 SPOE_EV_ON_SERVER_SESS,
133 SPOE_EV_ON_TCP_RSP,
134 SPOE_EV_ON_HTTP_RSP,
135
136 SPOE_EV_EVENTS
137};
138
Christopher Fauletb067b062017-01-04 16:39:11 +0100139/* Errors triggered by streams */
140enum spoe_context_error {
141 SPOE_CTX_ERR_NONE = 0,
142 SPOE_CTX_ERR_TOUT,
143 SPOE_CTX_ERR_RES,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100144 SPOE_CTX_ERR_TOO_BIG,
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100145 SPOE_CTX_ERR_FRAG_FRAME_ABRT,
Christopher Fauletb067b062017-01-04 16:39:11 +0100146 SPOE_CTX_ERR_UNKNOWN = 255,
147 SPOE_CTX_ERRS,
148};
149
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200150/* Errors triggerd by SPOE applet */
151enum spoe_frame_error {
152 SPOE_FRM_ERR_NONE = 0,
153 SPOE_FRM_ERR_IO,
154 SPOE_FRM_ERR_TOUT,
155 SPOE_FRM_ERR_TOO_BIG,
156 SPOE_FRM_ERR_INVALID,
157 SPOE_FRM_ERR_NO_VSN,
158 SPOE_FRM_ERR_NO_FRAME_SIZE,
159 SPOE_FRM_ERR_NO_CAP,
160 SPOE_FRM_ERR_BAD_VSN,
161 SPOE_FRM_ERR_BAD_FRAME_SIZE,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100162 SPOE_FRM_ERR_FRAG_NOT_SUPPORTED,
163 SPOE_FRM_ERR_INTERLACED_FRAMES,
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100164 SPOE_FRM_ERR_FRAMEID_NOTFOUND,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100165 SPOE_FRM_ERR_RES,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200166 SPOE_FRM_ERR_UNKNOWN = 99,
167 SPOE_FRM_ERRS,
168};
169
170/* Scopes used for variables set by agents. It is a way to be agnotic to vars
171 * scope. */
172enum spoe_vars_scope {
173 SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
174 SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
175 SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
176 SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
177 SPOE_SCOPE_RES, /* <=> SCOPE_RES */
178};
179
180
181/* Describe an argument that will be linked to a message. It is a sample fetch,
182 * with an optional name. */
183struct spoe_arg {
184 char *name; /* Name of the argument, may be NULL */
185 unsigned int name_len; /* The name length, 0 if NULL */
186 struct sample_expr *expr; /* Sample expression */
187 struct list list; /* Used to chain SPOE args */
188};
189
190/* Used during the config parsing only because, when a SPOE agent section is
191 * parsed, messages can be undefined. */
192struct spoe_msg_placeholder {
193 char *id; /* SPOE message placeholder id */
194 struct list list; /* Use to chain SPOE message placeholders */
195};
196
197/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
198 * an argument list (see above) and it is linked to a specific event. */
199struct spoe_message {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100200 char *id; /* SPOE message id */
201 unsigned int id_len; /* The message id length */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200202 struct spoe_agent *agent; /* SPOE agent owning this SPOE message */
203 struct {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100204 char *file; /* file where the SPOE message appears */
205 int line; /* line where the SPOE message appears */
206 } conf; /* config information */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100207 unsigned int nargs; /* # of arguments */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100208 struct list args; /* Arguments added when the SPOE messages is sent */
209 struct list list; /* Used to chain SPOE messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200210
211 enum spoe_event event; /* SPOE_EV_* */
212};
213
214/* Describe a SPOE agent. */
215struct spoe_agent {
216 char *id; /* SPOE agent id (name) */
217 struct {
218 char *file; /* file where the SPOE agent appears */
219 int line; /* line where the SPOE agent appears */
220 } conf; /* config information */
221 union {
222 struct proxy *be; /* Backend used by this agent */
223 char *name; /* Backend name used during conf parsing */
224 } b;
225 struct {
Christopher Fauletf7a30922016-11-10 15:04:51 +0100226 unsigned int hello; /* Max time to receive AGENT-HELLO frame (in SPOE applet) */
227 unsigned int idle; /* Max Idle timeout (in SPOE applet) */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100228 unsigned int processing; /* Max time to process an event (in the main stream) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200229 } timeout;
230
Christopher Fauleta1cda022016-12-21 08:58:06 +0100231 /* Config info */
232 char *engine_id; /* engine-id string */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200233 char *var_pfx; /* Prefix used for vars set by the agent */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100234 char *var_on_error; /* Variable to set when an error occurred, in the TXN scope */
Christopher Fauletea62c2a2016-11-14 10:54:21 +0100235 unsigned int flags; /* SPOE_FL_* */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100236 unsigned int cps_max; /* Maximum # of connections per second */
237 unsigned int eps_max; /* Maximum # of errors per second */
238 unsigned int max_frame_size; /* Maximum frame size for this agent, before any negotiation */
239 unsigned int min_applets; /* Minimum # applets alive at a time */
240 unsigned int max_fpa; /* Maximum # of frames handled per applet at once */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200241
242 struct list messages[SPOE_EV_EVENTS]; /* List of SPOE messages that will be sent
243 * for each supported events */
244
Christopher Fauleta1cda022016-12-21 08:58:06 +0100245 /* running info */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +0100246 unsigned int frame_size; /* current maximum frame size, only used to encode messages */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100247 unsigned int applets_act; /* # of applets alive at a time */
248 unsigned int applets_idle; /* # of applets in the state SPOE_APPCTX_ST_IDLE */
249 unsigned int sending_rate; /* the global sending rate */
250
251 struct freq_ctr conn_per_sec; /* connections per second */
252 struct freq_ctr err_per_sec; /* connetion errors per second */
253
254 struct list applets; /* List of available SPOE applets */
255 struct list sending_queue; /* Queue of streams waiting to send data */
256 struct list waiting_queue; /* Queue of streams waiting for a ack, in async mode */
257
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200258};
259
260/* SPOE filter configuration */
261struct spoe_config {
262 struct proxy *proxy; /* Proxy owning the filter */
263 struct spoe_agent *agent; /* Agent used by this filter */
264 struct proxy agent_fe; /* Agent frontend */
265};
266
267/* SPOE context attached to a stream. It is the main structure that handles the
268 * processing offload */
269struct spoe_context {
270 struct filter *filter; /* The SPOE filter */
271 struct stream *strm; /* The stream that should be offloaded */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100272
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200273 struct list *messages; /* List of messages that will be sent during the stream processing */
Christopher Faulet4596fb72017-01-11 14:05:19 +0100274 struct buffer *buffer; /* Buffer used to store a encoded messages */
275 struct buffer_wait buffer_wait; /* position in the list of ressources waiting for a buffer */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100276 struct list list;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200277
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200278 enum spoe_ctx_state state; /* SPOE_CTX_ST_* */
279 unsigned int flags; /* SPOE_CTX_FL_* */
Christopher Fauletb067b062017-01-04 16:39:11 +0100280 unsigned int status_code; /* SPOE_CTX_ERR_* */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200281
282 unsigned int stream_id; /* stream_id and frame_id are used */
283 unsigned int frame_id; /* to map NOTIFY and ACK frames */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100284 unsigned int process_exp; /* expiration date to process an event */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100285
286 struct {
287 struct spoe_appctx *spoe_appctx; /* SPOE appctx sending the fragmented frame */
288 struct spoe_message *curmsg; /* SPOE message from which to resume encoding */
289 struct spoe_arg *curarg; /* SPOE arg in <curmsg> from which to resume encoding */
290 unsigned int curoff; /* offset in <curarg> from which to resume encoding */
291 unsigned int flags; /* SPOE_FRM_FL_* */
292 } frag_ctx; /* Info about fragmented frames, valid on if SPOE_CTX_FL_FRAGMENTED is set */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200293};
294
Christopher Faulet42bfa462017-01-04 14:14:19 +0100295/* SPOE context inside a appctx */
296struct spoe_appctx {
297 struct appctx *owner; /* the owner */
298 struct task *task; /* task to handle applet timeouts */
299 struct spoe_agent *agent; /* agent on which the applet is attached */
300
301 unsigned int version; /* the negotiated version */
302 unsigned int max_frame_size; /* the negotiated max-frame-size value */
303 unsigned int flags; /* SPOE_APPCTX_FL_* */
304
Christopher Fauletb067b062017-01-04 16:39:11 +0100305 unsigned int status_code; /* SPOE_FRM_ERR_* */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100306#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
307 char *reason; /* Error message, used for debugging only */
308 int rlen; /* reason length */
309#endif
310
Christopher Faulet4596fb72017-01-11 14:05:19 +0100311 struct buffer *buffer; /* Buffer used to store a encoded messages */
312 struct buffer_wait buffer_wait; /* position in the list of ressources waiting for a buffer */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100313 struct list waiting_queue; /* list of streams waiting for a ACK frame, in sync and pipelining mode */
314 struct list list; /* next spoe appctx for the same agent */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100315
316 struct {
317 struct spoe_context *ctx; /* SPOE context owning the fragmented frame */
318 unsigned int cursid; /* stream-id of the fragmented frame. used if the processing is aborted */
319 unsigned int curfid; /* frame-id of the fragmented frame. used if the processing is aborted */
320 } frag_ctx; /* Info about fragmented frames, unused for unfragmented frames */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100321};
322
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100323
324/* Helper to get SPOE ctx inside an appctx */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100325#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
326
Christopher Faulet3b386a32017-02-23 10:17:15 +0100327/* SPOE filter id. Used to identify SPOE filters */
328const char *spoe_filter_id = "SPOE filter";
329
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200330/* Set if the handle on SIGUSR1 is registered */
331static int sighandler_registered = 0;
332
333/* proxy used during the parsing */
334struct proxy *curproxy = NULL;
335
336/* The name of the SPOE engine, used during the parsing */
337char *curengine = NULL;
338
339/* SPOE agent used during the parsing */
340struct spoe_agent *curagent = NULL;
341
342/* SPOE message used during the parsing */
343struct spoe_message *curmsg = NULL;
344
345/* list of SPOE messages and placeholders used during the parsing */
346struct list curmsgs;
347struct list curmps;
348
Christopher Faulet42bfa462017-01-04 14:14:19 +0100349/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200350static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100351static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200352
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200353struct flt_ops spoe_ops;
354
Christopher Faulet8ef75252017-02-20 22:56:03 +0100355static int spoe_queue_context(struct spoe_context *ctx);
356static int spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
357static void spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200358
359/********************************************************************
360 * helper functions/globals
361 ********************************************************************/
362static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100363spoe_release_msg_placeholder(struct spoe_msg_placeholder *mp)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200364{
365 if (!mp)
366 return;
367 free(mp->id);
368 free(mp);
369}
370
371
372static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100373spoe_release_message(struct spoe_message *msg)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200374{
375 struct spoe_arg *arg, *back;
376
377 if (!msg)
378 return;
379 free(msg->id);
380 free(msg->conf.file);
381 list_for_each_entry_safe(arg, back, &msg->args, list) {
382 release_sample_expr(arg->expr);
383 free(arg->name);
384 LIST_DEL(&arg->list);
385 free(arg);
386 }
387 free(msg);
388}
389
390static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100391spoe_release_agent(struct spoe_agent *agent)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200392{
393 struct spoe_message *msg, *back;
394 int i;
395
396 if (!agent)
397 return;
398 free(agent->id);
399 free(agent->conf.file);
400 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100401 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100402 free(agent->var_on_error);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200403 for (i = 0; i < SPOE_EV_EVENTS; ++i) {
404 list_for_each_entry_safe(msg, back, &agent->messages[i], list) {
405 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100406 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200407 }
408 }
409 free(agent);
410}
411
412static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100413 [SPOE_FRM_ERR_NONE] = "normal",
414 [SPOE_FRM_ERR_IO] = "I/O error",
415 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
416 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
417 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
418 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
419 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
420 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
421 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
422 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
423 [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
424 [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100425 [SPOE_FRM_ERR_FRAMEID_NOTFOUND] = "frame-id not found",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100426 [SPOE_FRM_ERR_RES] = "resource allocation error",
427 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200428};
429
430static const char *spoe_event_str[SPOE_EV_EVENTS] = {
431 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
432 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
433 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
434 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
435 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
436
437 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
438 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
439 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
440};
441
442
443#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
444
445static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100446 [SPOE_CTX_ST_NONE] = "NONE",
447 [SPOE_CTX_ST_READY] = "READY",
448 [SPOE_CTX_ST_ENCODING_MSGS] = "ENCODING_MSGS",
449 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
450 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
451 [SPOE_CTX_ST_DONE] = "DONE",
452 [SPOE_CTX_ST_ERROR] = "ERROR",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200453};
454
455static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100456 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
457 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
458 [SPOE_APPCTX_ST_IDLE] = "IDLE",
459 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
460 [SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY] = "SENDING_FRAG_NOTIFY",
461 [SPOE_APPCTX_ST_WAITING_SYNC_ACK] = "WAITING_SYNC_ACK",
462 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
463 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
464 [SPOE_APPCTX_ST_EXIT] = "EXIT",
465 [SPOE_APPCTX_ST_END] = "END",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200466};
467
468#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100469
Christopher Faulet8ef75252017-02-20 22:56:03 +0100470/* Used to generates a unique id for an engine. On success, it returns a
471 * allocated string. So it is the caller's reponsibility to release it. If the
472 * allocation failed, it returns NULL. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100473static char *
474generate_pseudo_uuid()
475{
476 static int init = 0;
477
478 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
479 const char uuid_chr[] = "0123456789ABCDEF-";
480 char *uuid;
481 int i;
482
483 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
484 return NULL;
485
486 if (!init) {
487 srand(now_ms);
488 init = 1;
489 }
490
491 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
492 int r = rand () % 16;
493
494 switch (uuid_fmt[i]) {
495 case 'x' : uuid[i] = uuid_chr[r]; break;
496 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
497 default : uuid[i] = uuid_fmt[i]; break;
498 }
499 }
500 return uuid;
501}
502
Christopher Faulet8ef75252017-02-20 22:56:03 +0100503/* Returns the minimum number of appets alive at a time. This function is used
504 * to know if more applets should be created for an engine. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100505static inline unsigned int
506min_applets_act(struct spoe_agent *agent)
507{
508 unsigned int nbsrv;
509
Christopher Faulet8ef75252017-02-20 22:56:03 +0100510 /* TODO: Add a config parameter to customize this value. Always 0 for
511 * now */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100512 if (agent->min_applets)
513 return agent->min_applets;
514
Christopher Faulet8ef75252017-02-20 22:56:03 +0100515 /* Get the number of active servers for the backend */
516 nbsrv = (agent->b.be->srv_act
517 ? agent->b.be->srv_act
518 : agent->b.be->srv_bck);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100519 return 2*nbsrv;
520}
521
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200522/********************************************************************
523 * Functions that encode/decode SPOE frames
524 ********************************************************************/
525/* Frame Types sent by HAProxy and by agents */
526enum spoe_frame_type {
527 /* Frames sent by HAProxy */
528 SPOE_FRM_T_HAPROXY_HELLO = 1,
529 SPOE_FRM_T_HAPROXY_DISCON,
530 SPOE_FRM_T_HAPROXY_NOTIFY,
531
532 /* Frames sent by the agents */
533 SPOE_FRM_T_AGENT_HELLO = 101,
534 SPOE_FRM_T_AGENT_DISCON,
535 SPOE_FRM_T_AGENT_ACK
536};
537
538/* All supported data types */
539enum spoe_data_type {
540 SPOE_DATA_T_NULL = 0,
541 SPOE_DATA_T_BOOL,
542 SPOE_DATA_T_INT32,
543 SPOE_DATA_T_UINT32,
544 SPOE_DATA_T_INT64,
545 SPOE_DATA_T_UINT64,
546 SPOE_DATA_T_IPV4,
547 SPOE_DATA_T_IPV6,
548 SPOE_DATA_T_STR,
549 SPOE_DATA_T_BIN,
550 SPOE_DATA_TYPES
551};
552
553/* Masks to get data type or flags value */
554#define SPOE_DATA_T_MASK 0x0F
555#define SPOE_DATA_FL_MASK 0xF0
556
557/* Flags to set Boolean values */
558#define SPOE_DATA_FL_FALSE 0x00
559#define SPOE_DATA_FL_TRUE 0x10
560
561/* Helper to get static string length, excluding the terminating null byte */
562#define SLEN(str) (sizeof(str)-1)
563
564/* Predefined key used in HELLO/DISCONNECT frames */
565#define SUPPORTED_VERSIONS_KEY "supported-versions"
566#define VERSION_KEY "version"
567#define MAX_FRAME_SIZE_KEY "max-frame-size"
568#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100569#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100570#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200571#define STATUS_CODE_KEY "status-code"
572#define MSG_KEY "message"
573
574struct spoe_version {
575 char *str;
576 int min;
577 int max;
578};
579
580/* All supported versions */
581static struct spoe_version supported_versions[] = {
582 {"1.0", 1000, 1000},
583 {NULL, 0, 0}
584};
585
586/* Comma-separated list of supported versions */
587#define SUPPORTED_VERSIONS_VAL "1.0"
588
589/* Comma-separated list of supported capabilities (none for now) */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100590#define CAPABILITIES_VAL "pipelining,async"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200591
Christopher Faulet8ef75252017-02-20 22:56:03 +0100592/* Convert a string to a SPOE version value. The string must follow the format
593 * "MAJOR.MINOR". It will be concerted into the integer (1000 * MAJOR + MINOR).
594 * If an error occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200595static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100596spoe_str_to_vsn(const char *str, size_t len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200597{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100598 const char *p, *end;
599 int maj, min, vsn;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200600
Christopher Faulet8ef75252017-02-20 22:56:03 +0100601 p = str;
602 end = str+len;
603 maj = min = 0;
604 vsn = -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200605
Christopher Faulet8ef75252017-02-20 22:56:03 +0100606 /* skip leading spaces */
607 while (p < end && isspace(*p))
608 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200609
Christopher Faulet8ef75252017-02-20 22:56:03 +0100610 /* parse Major number, until the '.' */
611 while (*p != '.') {
612 if (p >= end || *p < '0' || *p > '9')
613 goto out;
614 maj *= 10;
615 maj += (*p - '0');
616 p++;
617 }
618
619 /* check Major version */
620 if (!maj)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200621 goto out;
622
Christopher Faulet8ef75252017-02-20 22:56:03 +0100623 p++; /* skip the '.' */
624 if (p >= end || *p < '0' || *p > '9') /* Minor number is missing */
625 goto out;
626
627 /* Parse Minor number */
628 while (p < end) {
629 if (*p < '0' || *p > '9')
630 break;
631 min *= 10;
632 min += (*p - '0');
633 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200634 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100635
636 /* check Minor number */
637 if (min > 999)
638 goto out;
639
640 /* skip trailing spaces */
641 while (p < end && isspace(*p))
642 p++;
643 if (p != end)
644 goto out;
645
646 vsn = maj * 1000 + min;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200647 out:
648 return vsn;
649}
650
Christopher Faulet8ef75252017-02-20 22:56:03 +0100651/* Encode the integer <i> into a varint (variable-length integer). The encoded
652 * value is copied in <*buf>. Here is the encoding format:
653 *
654 * 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ]
655 * 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ]
656 * 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ]
657 * 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ]
658 * 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ]
659 * ...
660 *
661 * On success, it returns the number of written bytes and <*buf> is moved after
662 * the encoded value. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200663static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100664spoe_encode_varint(uint64_t i, char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200665{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100666 unsigned char *p = (unsigned char *)*buf;
667 int r;
668
669 if (p >= (unsigned char *)end)
670 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200671
672 if (i < 240) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100673 *p++ = i;
674 *buf = (char *)p;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200675 return 1;
676 }
677
Christopher Faulet8ef75252017-02-20 22:56:03 +0100678 *p++ = (unsigned char)i | 240;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200679 i = (i - 240) >> 4;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100680 while (i >= 128) {
681 if (p >= (unsigned char *)end)
682 return -1;
683 *p++ = (unsigned char)i | 128;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200684 i = (i - 128) >> 7;
685 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100686
687 if (p >= (unsigned char *)end)
688 return -1;
689 *p++ = (unsigned char)i;
690
691 r = ((char *)p - *buf);
692 *buf = (char *)p;
693 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200694}
695
Christopher Faulet8ef75252017-02-20 22:56:03 +0100696/* Decode a varint from <*buf> and save the decoded value in <*i>. See
697 * 'spoe_encode_varint' for details about varint.
698 * On success, it returns the number of read bytes and <*buf> is moved after the
699 * varint. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200700static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100701spoe_decode_varint(char **buf, char *end, uint64_t *i)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200702{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100703 unsigned char *p = (unsigned char *)*buf;
704 int r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200705
Christopher Faulet8ef75252017-02-20 22:56:03 +0100706 if (p >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200707 return -1;
708
Christopher Faulet8ef75252017-02-20 22:56:03 +0100709 *i = *p++;
710 if (*i < 240) {
711 *buf = (char *)p;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200712 return 1;
713 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100714
715 r = 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200716 do {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100717 if (p >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200718 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100719 *i += (uint64_t)*p << r;
720 r += 7;
721 } while (*p++ >= 128);
722
723 r = ((char *)p - *buf);
724 *buf = (char *)p;
725 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200726}
727
Christopher Faulet8ef75252017-02-20 22:56:03 +0100728/* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy
729 * of <str>. It must have enough space in <*buf> to encode the buffer, else an
730 * error is triggered.
731 * On success, it returns <len> and <*buf> is moved after the encoded value. If
732 * an error occurred, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200733static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100734spoe_encode_buffer(const char *str, size_t len, char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200735{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100736 char *p = *buf;
737 int ret;
738
739 if (p >= end)
740 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200741
742 if (!len) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100743 *p++ = 0;
744 *buf = p;
745 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200746 }
747
Christopher Faulet8ef75252017-02-20 22:56:03 +0100748 ret = spoe_encode_varint(len, &p, end);
749 if (ret == -1 || p + len > end)
750 return -1;
751
752 memcpy(p, str, len);
753 *buf = p + len;
754 return len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200755}
756
Christopher Faulet8ef75252017-02-20 22:56:03 +0100757/* Encode a buffer, possibly partially. It does the same thing than
758 * 'spoe_encode_buffer', but if there is not enough space, it does not fail.
759 * On success, it returns the number of copied bytes and <*buf> is moved after
760 * the encoded value. If an error occured, it returns -1. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100761static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100762spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100763{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100764 char *p = *buf;
765 int ret;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100766
Christopher Faulet8ef75252017-02-20 22:56:03 +0100767 if (p >= end)
768 return -1;
769
770 if (!len) {
771 *p++ = 0;
772 *buf = p;
773 return 0;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100774 }
775
Christopher Faulet8ef75252017-02-20 22:56:03 +0100776 ret = spoe_encode_varint(len, &p, end);
777 if (ret == -1 || p >= end)
778 return -1;
779
780 ret = (p+len < end) ? len : (end - p);
781 memcpy(p, str, ret);
782 *buf = p + ret;
783 return ret;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100784}
785
Christopher Faulet8ef75252017-02-20 22:56:03 +0100786/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str>
787 * points on the first byte of the buffer.
788 * On success, it returns the buffer length and <*buf> is moved after the
789 * encoded buffer. Otherwise, it returns -1. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200790static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100791spoe_decode_buffer(char **buf, char *end, char **str, size_t *len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200792{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100793 char *p = *buf;
794 uint64_t sz;
795 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200796
797 *str = NULL;
798 *len = 0;
799
Christopher Faulet8ef75252017-02-20 22:56:03 +0100800 ret = spoe_decode_varint(&p, end, &sz);
801 if (ret == -1 || p + sz > end)
802 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200803
Christopher Faulet8ef75252017-02-20 22:56:03 +0100804 *str = p;
805 *len = sz;
806 *buf = p + sz;
807 return sz;
808}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200809
Christopher Faulet8ef75252017-02-20 22:56:03 +0100810/* Encode a typed data using value in <smp>. On success, it returns the number
811 * of copied bytes and <*buf> is moved after the encoded value. If an error
812 * occured, it returns -1.
813 *
814 * If the value is too big to be encoded, depending on its type, then encoding
815 * failed or the value is partially encoded. Only strings and binaries can be
816 * partially encoded. In this case, the offset <*off> is updated to known how
817 * many bytes has been encoded. If <*off> is zero at the end, it means that all
818 * data has been encoded. */
819static int
820spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end)
821{
822 char *p = *buf;
823 int ret;
824
825 if (p >= end)
826 return -1;
827
828 if (smp == NULL) {
829 *p++ = SPOE_DATA_T_NULL;
830 goto end;
831 }
832
833 switch (smp->data.type) {
834 case SMP_T_BOOL:
835 *p = SPOE_DATA_T_BOOL;
836 *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
837 break;
838
839 case SMP_T_SINT:
840 *p++ = SPOE_DATA_T_INT64;
841 if (spoe_encode_varint(smp->data.u.sint, &p, end) == -1)
842 return -1;
843 break;
844
845 case SMP_T_IPV4:
846 if (p + 5 > end)
847 return -1;
848 *p++ = SPOE_DATA_T_IPV4;
849 memcpy(p, &smp->data.u.ipv4, 4);
850 p += 4;
851 break;
852
853 case SMP_T_IPV6:
854 if (p + 17 > end)
855 return -1;
856 *p++ = SPOE_DATA_T_IPV6;
857 memcpy(p, &smp->data.u.ipv6, 16);
858 p += 16;
859 break;
860
861 case SMP_T_STR:
862 case SMP_T_BIN: {
863 struct chunk *chk = &smp->data.u.str;
864
865 /* Here, we need to know if the sample has already been
866 * partially encoded. If yes, we only need to encode the
867 * remaining, <*off> reprensenting the number of bytes
868 * already encoded. */
869 if (!*off) {
870 /* First evaluation of the sample : encode the
871 * type (string or binary), the buffer length
872 * (as a varint) and at least 1 byte of the
873 * buffer. */
874 struct chunk *chk = &smp->data.u.str;
875
876 *p++ = (smp->data.type == SMP_T_STR)
877 ? SPOE_DATA_T_STR
878 : SPOE_DATA_T_BIN;
879 ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end);
880 if (ret == -1)
881 return -1;
882 }
883 else {
884 /* The sample has been fragmented, encode remaining data */
885 ret = MIN(chk->len - *off, end - p);
886 memcpy(p, chk->str + *off, ret);
887 p += ret;
888 }
889 /* Now update <*off> */
890 if (ret + *off != chk->len)
891 *off += ret;
892 else
893 *off = 0;
894 break;
895 }
896
897 case SMP_T_METH:
898 *p++ = SPOE_DATA_T_STR;
899 if (smp->data.u.meth.meth != HTTP_METH_OTHER) {
900 const struct http_method_name *meth =
901 &http_known_methods[smp->data.u.meth.meth];
902
903 if (spoe_encode_buffer(meth->name, meth->len, &p, end) == -1)
904 return -1;
905 }
906 else {
907 struct chunk *meth = &smp->data.u.meth.str;
908
909 if (spoe_encode_buffer(meth->str, meth->len, &p, end) == -1)
910 return -1;
911 }
912 break;
913
914 default:
915 *p++ = SPOE_DATA_T_NULL;
916 break;
917 }
918
919 end:
920 ret = (p - *buf);
921 *buf = p;
922 return ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200923}
924
925/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
Christopher Faulet8ef75252017-02-20 22:56:03 +0100926 * of skipped bytes is returned and the <*buf> is moved after skipped data.
927 *
928 * A types data is composed of a type (1 byte) and corresponding data:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200929 * - boolean: non additional data (0 bytes)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100930 * - integers: a variable-length integer (see spoe_decode_varint)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200931 * - ipv4: 4 bytes
932 * - ipv6: 16 bytes
933 * - binary and string: a buffer prefixed by its size, a variable-length
Christopher Faulet8ef75252017-02-20 22:56:03 +0100934 * integer (see spoe_decode_buffer) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200935static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100936spoe_skip_data(char **buf, char *end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200937{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100938 char *str, *p = *buf;
939 int type, ret;
940 size_t sz;
941 uint64_t v;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200942
Christopher Faulet8ef75252017-02-20 22:56:03 +0100943 if (p >= end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200944 return -1;
945
Christopher Faulet8ef75252017-02-20 22:56:03 +0100946 type = *p++;
947 switch (type & SPOE_DATA_T_MASK) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200948 case SPOE_DATA_T_BOOL:
949 break;
950 case SPOE_DATA_T_INT32:
951 case SPOE_DATA_T_INT64:
952 case SPOE_DATA_T_UINT32:
953 case SPOE_DATA_T_UINT64:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100954 if (spoe_decode_varint(&p, end, &v) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200955 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200956 break;
957 case SPOE_DATA_T_IPV4:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100958 if (p+4 > end)
959 return -1;
960 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200961 break;
962 case SPOE_DATA_T_IPV6:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100963 if (p+16 > end)
964 return -1;
965 p += 16;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200966 break;
967 case SPOE_DATA_T_STR:
968 case SPOE_DATA_T_BIN:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100969 /* All the buffer must be skipped */
970 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200971 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200972 break;
973 }
974
Christopher Faulet8ef75252017-02-20 22:56:03 +0100975 ret = (p - *buf);
976 *buf = p;
977 return ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200978}
979
Christopher Faulet8ef75252017-02-20 22:56:03 +0100980/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
981 * otherwise the number of read bytes is returned and <*buf> is moved after the
982 * decoded data. See spoe_skip_data for details. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200983static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100984spoe_decode_data(char **buf, char *end, struct sample *smp)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200985{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100986 char *str, *p = *buf;
987 int type, r = 0;
988 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200989
Christopher Faulet8ef75252017-02-20 22:56:03 +0100990 if (p >= end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200991 return -1;
992
Christopher Faulet8ef75252017-02-20 22:56:03 +0100993 type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200994 switch (type & SPOE_DATA_T_MASK) {
995 case SPOE_DATA_T_BOOL:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100996 smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200997 smp->data.type = SMP_T_BOOL;
998 break;
999 case SPOE_DATA_T_INT32:
1000 case SPOE_DATA_T_INT64:
1001 case SPOE_DATA_T_UINT32:
1002 case SPOE_DATA_T_UINT64:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001003 if (spoe_decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001004 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001005 smp->data.type = SMP_T_SINT;
1006 break;
1007 case SPOE_DATA_T_IPV4:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001008 if (p+4 > end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001009 return -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001010 smp->data.type = SMP_T_IPV4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001011 memcpy(&smp->data.u.ipv4, p, 4);
1012 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001013 break;
1014 case SPOE_DATA_T_IPV6:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001015 if (p+16 > end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001016 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001017 memcpy(&smp->data.u.ipv6, p, 16);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001018 smp->data.type = SMP_T_IPV6;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001019 p += 16;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001020 break;
1021 case SPOE_DATA_T_STR:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001022 case SPOE_DATA_T_BIN:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001023 /* All the buffer must be decoded */
1024 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001025 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001026 smp->data.u.str.str = str;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001027 smp->data.u.str.len = sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001028 smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001029 break;
1030 }
1031
Christopher Faulet8ef75252017-02-20 22:56:03 +01001032 r = (p - *buf);
1033 *buf = p;
1034 return r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001035}
1036
Christopher Faulet8ef75252017-02-20 22:56:03 +01001037/* Encode the HELLO frame sent by HAProxy to an agent. It returns the number of
1038 * encoded bytes in the frame on success, 0 if an encoding error occured and -1
1039 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001040static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001041spoe_prepare_hahello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001042{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001043 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001044 char *p, *end;
1045 unsigned int flags = SPOE_FRM_FL_FIN;
1046 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001047
Christopher Faulet8ef75252017-02-20 22:56:03 +01001048 p = frame;
1049 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001050
Christopher Faulet8ef75252017-02-20 22:56:03 +01001051 /* Set Frame type */
1052 *p++ = SPOE_FRM_T_HAPROXY_HELLO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001053
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001054 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001055 memcpy(p, (char *)&flags, 4);
1056 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001057
1058 /* No stream-id and frame-id for HELLO frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001059 *p++ = 0; *p++ = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001060
1061 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
1062 * and "capabilities" */
1063
1064 /* "supported-versions" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001065 sz = SLEN(SUPPORTED_VERSIONS_KEY);
1066 if (spoe_encode_buffer(SUPPORTED_VERSIONS_KEY, sz, &p, end) == -1)
1067 goto too_big;
1068
1069 *p++ = SPOE_DATA_T_STR;
1070 sz = SLEN(SUPPORTED_VERSIONS_VAL);
1071 if (spoe_encode_buffer(SUPPORTED_VERSIONS_VAL, sz, &p, end) == -1)
1072 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001073
1074 /* "max-fram-size" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001075 sz = SLEN(MAX_FRAME_SIZE_KEY);
1076 if (spoe_encode_buffer(MAX_FRAME_SIZE_KEY, sz, &p, end) == -1)
1077 goto too_big;
1078
1079 *p++ = SPOE_DATA_T_UINT32;
1080 if (spoe_encode_varint(SPOE_APPCTX(appctx)->max_frame_size, &p, end) == -1)
1081 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001082
1083 /* "capabilities" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001084 sz = SLEN(CAPABILITIES_KEY);
1085 if (spoe_encode_buffer(CAPABILITIES_KEY, sz, &p, end) == -1)
1086 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001087
Christopher Faulet8ef75252017-02-20 22:56:03 +01001088 *p++ = SPOE_DATA_T_STR;
1089 sz = SLEN(CAPABILITIES_VAL);
1090 if (spoe_encode_buffer(CAPABILITIES_VAL, sz, &p, end) == -1)
1091 goto too_big;
1092
1093 /* (optionnal) "engine-id" K/V item, if present */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001094 if (agent != NULL && agent->engine_id != NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001095 sz = SLEN(ENGINE_ID_KEY);
1096 if (spoe_encode_buffer(ENGINE_ID_KEY, sz, &p, end) == -1)
1097 goto too_big;
1098
1099 *p++ = SPOE_DATA_T_STR;
1100 sz = strlen(agent->engine_id);
1101 if (spoe_encode_buffer(agent->engine_id, sz, &p, end) == -1)
1102 goto too_big;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001103 }
1104
Christopher Faulet8ef75252017-02-20 22:56:03 +01001105 return (p - frame);
1106
1107 too_big:
1108 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
1109 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001110}
1111
Christopher Faulet8ef75252017-02-20 22:56:03 +01001112/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the number of
1113 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
1114 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001115static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001116spoe_prepare_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001117{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001118 const char *reason;
1119 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001120 unsigned int flags = SPOE_FRM_FL_FIN;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001121 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001122
Christopher Faulet8ef75252017-02-20 22:56:03 +01001123 p = frame;
1124 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001125
Christopher Faulet8ef75252017-02-20 22:56:03 +01001126 /* Set Frame type */
1127 *p++ = SPOE_FRM_T_HAPROXY_DISCON;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001128
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001129 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001130 memcpy(p, (char *)&flags, 4);
1131 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001132
1133 /* No stream-id and frame-id for DISCONNECT frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001134 *p++ = 0; *p++ = 0;
1135
1136 if (SPOE_APPCTX(appctx)->status_code >= SPOE_FRM_ERRS)
1137 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001138
1139 /* There are 2 mandatory items: "status-code" and "message" */
1140
1141 /* "status-code" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001142 sz = SLEN(STATUS_CODE_KEY);
1143 if (spoe_encode_buffer(STATUS_CODE_KEY, sz, &p, end) == -1)
1144 goto too_big;
1145
1146 *p++ = SPOE_DATA_T_UINT32;
1147 if (spoe_encode_varint(SPOE_APPCTX(appctx)->status_code, &p, end) == -1)
1148 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001149
1150 /* "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001151 sz = SLEN(MSG_KEY);
1152 if (spoe_encode_buffer(MSG_KEY, sz, &p, end) == -1)
1153 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001154
Christopher Faulet8ef75252017-02-20 22:56:03 +01001155 /*Get the message corresponding to the status code */
1156 reason = spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code];
1157
1158 *p++ = SPOE_DATA_T_STR;
1159 sz = strlen(reason);
1160 if (spoe_encode_buffer(reason, sz, &p, end) == -1)
1161 goto too_big;
1162
1163 return (p - frame);
1164
1165 too_big:
1166 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
1167 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001168}
1169
Christopher Faulet8ef75252017-02-20 22:56:03 +01001170/* Encode the NOTIFY frame sent by HAProxy to an agent. It returns the number of
1171 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
1172 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001173static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001174spoe_prepare_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001175 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001176{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001177 char *p, *end;
1178 unsigned int stream_id, frame_id;
1179 unsigned int flags = SPOE_FRM_FL_FIN;
1180 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001181
Christopher Faulet8ef75252017-02-20 22:56:03 +01001182 p = frame;
1183 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001184
Christopher Faulet8ef75252017-02-20 22:56:03 +01001185 /* <ctx> is null when the stream has aborted the processing of a
1186 * fragmented frame. In this case, we must notify the corresponding
1187 * agent using ids stored in <frag_ctx>. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001188 if (ctx == NULL) {
1189 flags |= SPOE_FRM_FL_ABRT;
1190 stream_id = SPOE_APPCTX(appctx)->frag_ctx.cursid;
1191 frame_id = SPOE_APPCTX(appctx)->frag_ctx.curfid;
1192 }
1193 else {
1194 stream_id = ctx->stream_id;
1195 frame_id = ctx->frame_id;
1196
1197 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001198 /* The fragmentation is not supported by the applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001199 if (!(SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_FRAGMENTATION)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001200 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
1201 return -1;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001202 }
1203 flags = ctx->frag_ctx.flags;
1204 }
1205 }
1206
Christopher Faulet8ef75252017-02-20 22:56:03 +01001207 /* Set Frame type */
1208 *p++ = SPOE_FRM_T_HAPROXY_NOTIFY;
1209
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001210 /* Set flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001211 memcpy(p, (char *)&flags, 4);
1212 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001213
1214 /* Set stream-id and frame-id */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001215 if (spoe_encode_varint(stream_id, &p, end) == -1)
1216 goto too_big;
1217 if (spoe_encode_varint(frame_id, &p, end) == -1)
1218 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001219
Christopher Faulet8ef75252017-02-20 22:56:03 +01001220 /* Copy encoded messages, if possible */
1221 sz = SPOE_APPCTX(appctx)->buffer->i;
1222 if (p + sz >= end)
1223 goto too_big;
1224 memcpy(p, SPOE_APPCTX(appctx)->buffer->p, sz);
1225 p += sz;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001226
Christopher Faulet8ef75252017-02-20 22:56:03 +01001227 return (p - frame);
1228
1229 too_big:
1230 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
1231 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001232}
1233
Christopher Faulet8ef75252017-02-20 22:56:03 +01001234/* Decode and process the HELLO frame sent by an agent. It returns the number of
1235 * read bytes on success, 0 if a decoding error occurred, and -1 if a fatal
1236 * error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001237static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001238spoe_handle_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001239{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001240 char *p, *end;
1241 int vsn, max_frame_size;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001242 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001243
1244 p = frame;
1245 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001246
1247 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001248 if (*p++ != SPOE_FRM_T_AGENT_HELLO) {
1249 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001250 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001251 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001252
Christopher Faulet8ef75252017-02-20 22:56:03 +01001253 if (size < 7 /* TYPE + METADATA */) {
1254 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1255 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001256 }
1257
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001258 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001259 memcpy((char *)&flags, p, 4);
1260 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001261
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001262 /* Fragmentation is not supported for HELLO frame */
1263 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001264 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001265 return -1;
1266 }
1267
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001268 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001269 if (*p != 0 || *(p+1) != 0) {
1270 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1271 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001272 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001273 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001274
1275 /* There are 3 mandatory items: "version", "max-frame-size" and
1276 * "capabilities" */
1277
1278 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001279 vsn = max_frame_size = flags = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001280 while (p < end) {
1281 char *str;
1282 size_t sz;
1283 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001284
1285 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001286 ret = spoe_decode_buffer(&p, end, &str, &sz);
1287 if (ret == -1 || !sz) {
1288 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1289 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001290 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001291
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001292 /* Check "version" K/V item */
1293 if (!memcmp(str, VERSION_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001294 int i, type = *p++;
1295
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001296 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001297 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1298 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1299 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001300 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001301 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
1302 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1303 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001304 }
1305
Christopher Faulet8ef75252017-02-20 22:56:03 +01001306 vsn = spoe_str_to_vsn(str, sz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001307 if (vsn == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001308 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001309 return -1;
1310 }
1311 for (i = 0; supported_versions[i].str != NULL; ++i) {
1312 if (vsn >= supported_versions[i].min &&
1313 vsn <= supported_versions[i].max)
1314 break;
1315 }
1316 if (supported_versions[i].str == NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001317 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001318 return -1;
1319 }
1320 }
1321 /* Check "max-frame-size" K/V item */
1322 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001323 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001324
1325 /* The value must be integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001326 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1327 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1328 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1329 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001330 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1331 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001332 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001333 if (spoe_decode_varint(&p, end, &sz) == -1) {
1334 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1335 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001336 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001337 if (sz < MIN_FRAME_SIZE ||
1338 sz > SPOE_APPCTX(appctx)->max_frame_size) {
1339 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001340 return -1;
1341 }
1342 max_frame_size = sz;
1343 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001344 /* Check "capabilities" K/V item */
1345 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001346 int type = *p++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001347
1348 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001349 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1350 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1351 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001352 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001353 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
1354 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1355 return 0;
1356 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001357
Christopher Faulet8ef75252017-02-20 22:56:03 +01001358 while (sz) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001359 char *delim;
1360
1361 /* Skip leading spaces */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001362 for (; isspace(*str) && sz; str++, sz--);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001363
Christopher Faulet8ef75252017-02-20 22:56:03 +01001364 if (sz >= 10 && !strncmp(str, "pipelining", 10)) {
1365 str += 10; sz -= 10;
1366 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +01001367 flags |= SPOE_APPCTX_FL_PIPELINING;
1368 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001369 else if (sz >= 5 && !strncmp(str, "async", 5)) {
1370 str += 5; sz -= 5;
1371 if (!sz || isspace(*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +01001372 flags |= SPOE_APPCTX_FL_ASYNC;
1373 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001374 else if (sz >= 13 && !strncmp(str, "fragmentation", 13)) {
1375 str += 13; sz -= 13;
1376 if (!sz || isspace(*str) || *str == ',')
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001377 flags |= SPOE_APPCTX_FL_FRAGMENTATION;
1378 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001379
Christopher Faulet8ef75252017-02-20 22:56:03 +01001380 /* Get the next comma or break */
1381 if (!sz || (delim = memchr(str, ',', sz)) == NULL)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001382 break;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001383 delim++;
1384 sz -= (delim - str);
1385 str = delim;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001386 }
1387 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001388 else {
1389 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001390 if (spoe_skip_data(&p, end) == -1) {
1391 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1392 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001393 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001394 }
1395 }
1396
1397 /* Final checks */
1398 if (!vsn) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001399 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001400 return -1;
1401 }
1402 if (!max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001403 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001404 return -1;
1405 }
1406
Christopher Faulet42bfa462017-01-04 14:14:19 +01001407 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
1408 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
1409 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001410
1411 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001412}
1413
1414/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
1415 * bytes on success, 0 if the frame can be ignored and -1 if an error
1416 * occurred. */
1417static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001418spoe_handle_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001419{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001420 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001421 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001422
1423 p = frame;
1424 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001425
1426 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001427 if (*p++ != SPOE_FRM_T_AGENT_DISCON) {
1428 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001429 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001430 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001431
Christopher Faulet8ef75252017-02-20 22:56:03 +01001432 if (size < 7 /* TYPE + METADATA */) {
1433 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1434 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001435 }
1436
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001437 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001438 memcpy((char *)&flags, p, 4);
1439 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001440
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001441 /* Fragmentation is not supported for DISCONNECT frame */
1442 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001443 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001444 return -1;
1445 }
1446
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001447 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001448 if (*p != 0 || *(p+1) != 0) {
1449 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1450 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001451 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001452 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001453
1454 /* There are 2 mandatory items: "status-code" and "message" */
1455
1456 /* Loop on K/V items */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001457 while (p < end) {
1458 char *str;
1459 size_t sz;
1460 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001461
1462 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001463 ret = spoe_decode_buffer(&p, end, &str, &sz);
1464 if (ret == -1 || !sz) {
1465 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1466 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001467 }
1468
1469 /* Check "status-code" K/V item */
1470 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001471 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001472
1473 /* The value must be an integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001474 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1475 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1476 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1477 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001478 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1479 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001480 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001481 if (spoe_decode_varint(&p, end, &sz) == -1) {
1482 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1483 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001484 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001485 SPOE_APPCTX(appctx)->status_code = sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001486 }
1487
1488 /* Check "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001489 else if (!memcmp(str, MSG_KEY, sz)) {
1490 int type = *p++;
1491
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001492 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001493 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1494 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1495 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001496 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001497 ret = spoe_decode_buffer(&p, end, &str, &sz);
1498 if (ret == -1 || sz > 255) {
1499 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1500 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001501 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001502#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
1503 SPOE_APPCTX(appctx)->reason = str;
1504 SPOE_APPCTX(appctx)->rlen = sz;
1505#endif
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001506 }
1507 else {
1508 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001509 if (spoe_skip_data(&p, end) == -1) {
1510 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1511 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001512 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001513 }
1514 }
1515
Christopher Faulet8ef75252017-02-20 22:56:03 +01001516 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001517}
1518
1519
Christopher Fauleta1cda022016-12-21 08:58:06 +01001520/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001521 * success, 0 if the frame can be ignored and -1 if an error occurred. */
1522static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001523spoe_handle_agentack_frame(struct appctx *appctx, struct spoe_context **ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001524 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001525{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001526 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001527 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001528 uint64_t stream_id, frame_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001529 int len;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001530 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001531
1532 p = frame;
1533 end = frame + size;
1534 *ctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001535
1536 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001537 if (*p++ != SPOE_FRM_T_AGENT_ACK) {
1538 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001539 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001540 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001541
Christopher Faulet8ef75252017-02-20 22:56:03 +01001542 if (size < 7 /* TYPE + METADATA */) {
1543 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1544 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001545 }
1546
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001547 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001548 memcpy((char *)&flags, p, 4);
1549 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001550
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001551 /* Fragmentation is not supported for now */
1552 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001553 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001554 return -1;
1555 }
1556
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001557 /* Get the stream-id and the frame-id */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001558 if (spoe_decode_varint(&p, end, &stream_id) == -1) {
1559 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001560 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001561 }
1562 if (spoe_decode_varint(&p, end, &frame_id) == -1) {
1563 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001564 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001565 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001566
Christopher Faulet8ef75252017-02-20 22:56:03 +01001567 /* Try to find the corresponding SPOE context */
Christopher Faulet42bfa462017-01-04 14:14:19 +01001568 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001569 list_for_each_entry((*ctx), &agent->waiting_queue, list) {
1570 if ((*ctx)->stream_id == (unsigned int)stream_id &&
1571 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001572 goto found;
1573 }
1574 }
1575 else {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001576 list_for_each_entry((*ctx), &SPOE_APPCTX(appctx)->waiting_queue, list) {
1577 if ((*ctx)->stream_id == (unsigned int)stream_id &&
Christopher Faulet8ef75252017-02-20 22:56:03 +01001578 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001579 goto found;
1580 }
1581 }
1582
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001583 if (SPOE_APPCTX(appctx)->frag_ctx.ctx &&
1584 SPOE_APPCTX(appctx)->frag_ctx.cursid == (unsigned int)stream_id &&
1585 SPOE_APPCTX(appctx)->frag_ctx.curfid == (unsigned int)frame_id) {
1586
1587 /* ABRT bit is set for an unfinished fragmented frame */
1588 if (flags & SPOE_FRM_FL_ABRT) {
1589 *ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1590 (*ctx)->frag_ctx.spoe_appctx = NULL;
1591 (*ctx)->state = SPOE_CTX_ST_ERROR;
1592 (*ctx)->status_code = SPOE_CTX_ERR_FRAG_FRAME_ABRT;
1593 /* Ignore the payload */
1594 goto end;
1595 }
1596 /* TODO: Handle more flags for fragmented frames: RESUME, FINISH... */
1597 /* For now, we ignore the ack */
1598 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1599 return 0;
1600 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001601
Christopher Fauleta1cda022016-12-21 08:58:06 +01001602 /* No Stream found, ignore the frame */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001603 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1604 " - Ignore ACK frame"
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001605 " - stream-id=%u - frame-id=%u\n",
1606 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1607 __FUNCTION__, appctx,
1608 (unsigned int)stream_id, (unsigned int)frame_id);
1609
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001610 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAMEID_NOTFOUND;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001611 return 0;
1612
1613 found:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001614 if (!spoe_acquire_buffer(&SPOE_APPCTX(appctx)->buffer,
1615 &SPOE_APPCTX(appctx)->buffer_wait)) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001616 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001617 return 1; /* Retry later */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001618 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001619
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001620 /* Copy encoded actions */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001621 len = (end - p);
1622 memcpy(SPOE_APPCTX(appctx)->buffer->p, p, len);
1623 SPOE_APPCTX(appctx)->buffer->i = len;
1624 p += len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001625
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001626 /* Transfer the buffer ownership to the SPOE context */
1627 (*ctx)->buffer = SPOE_APPCTX(appctx)->buffer;
1628 SPOE_APPCTX(appctx)->buffer = &buf_empty;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001629
Christopher Faulet8ef75252017-02-20 22:56:03 +01001630 (*ctx)->state = SPOE_CTX_ST_DONE;
1631
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001632 end:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001633 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001634 " - ACK frame received"
1635 " - ctx=%p - stream-id=%u - frame-id=%u - flags=0x%08x\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001636 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001637 __FUNCTION__, appctx, *ctx, (*ctx)->stream_id,
1638 (*ctx)->frame_id, flags);
1639 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001640}
1641
Christopher Fauletba7bc162016-11-07 21:07:38 +01001642/* This function is used in cfgparse.c and declared in proto/checks.h. It
1643 * prepare the request to send to agents during a healthcheck. It returns 0 on
1644 * success and -1 if an error occurred. */
1645int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001646spoe_prepare_healthcheck_request(char **req, int *len)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001647{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001648 struct appctx appctx;
1649 struct spoe_appctx spoe_appctx;
1650 char *frame, *end, buf[MAX_FRAME_SIZE+4];
1651 size_t sz;
1652 int ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001653
Christopher Faulet42bfa462017-01-04 14:14:19 +01001654 memset(&appctx, 0, sizeof(appctx));
1655 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001656 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001657
1658 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001659 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001660
Christopher Faulet8ef75252017-02-20 22:56:03 +01001661 frame = buf+4; /* Reserved the 4 first bytes for the frame size */
1662 end = frame + MAX_FRAME_SIZE;
1663
1664 ret = spoe_prepare_hahello_frame(&appctx, frame, MAX_FRAME_SIZE);
1665 if (ret <= 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001666 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001667 frame += ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001668
Christopher Faulet8ef75252017-02-20 22:56:03 +01001669 /* Add "healthcheck" K/V item */
1670 sz = SLEN(HEALTHCHECK_KEY);
1671 if (spoe_encode_buffer(HEALTHCHECK_KEY, sz, &frame, end) == -1)
1672 return -1;
1673 *frame++ = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001674
Christopher Faulet8ef75252017-02-20 22:56:03 +01001675 *len = frame - buf;
1676 sz = htonl(*len - 4);
1677 memcpy(buf, (char *)&sz, 4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001678
Christopher Faulet8ef75252017-02-20 22:56:03 +01001679 if ((*req = malloc(*len)) == NULL)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001680 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001681 memcpy(*req, buf, *len);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001682 return 0;
1683}
1684
1685/* This function is used in checks.c and declared in proto/checks.h. It decode
1686 * the response received from an agent during a healthcheck. It returns 0 on
1687 * success and -1 if an error occurred. */
1688int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001689spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001690{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001691 struct appctx appctx;
1692 struct spoe_appctx spoe_appctx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001693
Christopher Faulet42bfa462017-01-04 14:14:19 +01001694 memset(&appctx, 0, sizeof(appctx));
1695 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001696
Christopher Faulet42bfa462017-01-04 14:14:19 +01001697 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001698 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001699
Christopher Faulet8ef75252017-02-20 22:56:03 +01001700 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1701 spoe_handle_agentdiscon_frame(&appctx, frame, size);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001702 goto error;
1703 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001704 if (spoe_handle_agenthello_frame(&appctx, frame, size) <= 0)
1705 goto error;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001706
1707 return 0;
1708
1709 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001710 if (SPOE_APPCTX(&appctx)->status_code >= SPOE_FRM_ERRS)
1711 SPOE_APPCTX(&appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
1712 strncpy(err, spoe_frm_err_reasons[SPOE_APPCTX(&appctx)->status_code], errlen);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001713 return -1;
1714}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001715
Christopher Fauleta1cda022016-12-21 08:58:06 +01001716/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1717 * the frame can be ignored, 1 to retry later, and the frame legnth on
1718 * success. */
1719static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001720spoe_send_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001721{
1722 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001723 int ret;
1724 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001725
1726 if (si_ic(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001727 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001728
Christopher Faulet8ef75252017-02-20 22:56:03 +01001729 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1730 * length. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001731 netint = htonl(framesz);
1732 memcpy(buf, (char *)&netint, 4);
1733 ret = bi_putblk(si_ic(si), buf, framesz+4);
1734
1735 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001736 if (ret == -1) {
1737 retry:
1738 si_applet_cant_put(si);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001739 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001740 }
1741 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001742 return -1; /* error */
1743 }
1744 return framesz;
1745}
1746
1747/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1748 * when the frame can be ignored, 1 to retry later and the frame length on
1749 * success. */
1750static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001751spoe_recv_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001752{
1753 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001754 int ret;
1755 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001756
1757 if (si_oc(si)->buf == &buf_empty)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001758 goto retry;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001759
1760 ret = bo_getblk(si_oc(si), (char *)&netint, 4, 0);
1761 if (ret > 0) {
1762 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001763 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001764 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001765 return -1;
1766 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001767 ret = bo_getblk(si_oc(si), buf, framesz, 4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001768 }
1769 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001770 if (ret == 0) {
1771 retry:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001772 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001773 }
1774 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001775 return -1; /* error */
1776 }
1777 return framesz;
1778}
1779
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001780/********************************************************************
1781 * Functions that manage the SPOE applet
1782 ********************************************************************/
Christopher Faulet4596fb72017-01-11 14:05:19 +01001783static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001784spoe_wakeup_appctx(struct appctx *appctx)
Christopher Faulet4596fb72017-01-11 14:05:19 +01001785{
1786 si_applet_want_get(appctx->owner);
1787 si_applet_want_put(appctx->owner);
1788 appctx_wakeup(appctx);
1789 return 1;
1790}
1791
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001792/* Callback function that catches applet timeouts. If a timeout occurred, we set
1793 * <appctx->st1> flag and the SPOE applet is woken up. */
1794static struct task *
Christopher Faulet8ef75252017-02-20 22:56:03 +01001795spoe_process_appctx(struct task * task)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001796{
1797 struct appctx *appctx = task->context;
1798
1799 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1800 if (tick_is_expired(task->expire, now_ms)) {
1801 task->expire = TICK_ETERNITY;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001802 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001803 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001804 spoe_wakeup_appctx(appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001805 return task;
1806}
1807
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001808/* Callback function that releases a SPOE applet. This happens when the
1809 * connection with the agent is closed. */
1810static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001811spoe_release_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001812{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001813 struct stream_interface *si = appctx->owner;
1814 struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
1815 struct spoe_agent *agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001816 struct spoe_context *ctx, *back;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001817
1818 if (spoe_appctx == NULL)
1819 return;
1820
1821 appctx->ctx.spoe.ptr = NULL;
1822 agent = spoe_appctx->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001823
1824 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1825 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1826 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001827
Christopher Faulet8ef75252017-02-20 22:56:03 +01001828 /* Remove applet from the list of running applets */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001829 agent->applets_act--;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001830 if (!LIST_ISEMPTY(&spoe_appctx->list)) {
1831 LIST_DEL(&spoe_appctx->list);
1832 LIST_INIT(&spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001833 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001834
Christopher Faulet8ef75252017-02-20 22:56:03 +01001835 /* Shutdown the server connection, if needed */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001836 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001837 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1838 agent->applets_idle--;
1839
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001840 si_shutw(si);
1841 si_shutr(si);
1842 si_ic(si)->flags |= CF_READ_NULL;
1843 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001844 if (spoe_appctx->status_code == SPOE_FRM_ERR_NONE)
1845 spoe_appctx->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001846 }
1847
Christopher Faulet8ef75252017-02-20 22:56:03 +01001848 /* Destroy the task attached to this applet */
1849 if (spoe_appctx->task) {
1850 task_delete(spoe_appctx->task);
1851 task_free(spoe_appctx->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001852 }
1853
Christopher Faulet8ef75252017-02-20 22:56:03 +01001854 /* Notify all waiting streams */
1855 list_for_each_entry_safe(ctx, back, &spoe_appctx->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001856 LIST_DEL(&ctx->list);
1857 LIST_INIT(&ctx->list);
1858 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001859 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001860 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001861 }
1862
Christopher Faulet8ef75252017-02-20 22:56:03 +01001863 /* If the applet was processing a fragmented frame, notify the
1864 * corresponding stream. */
1865 if (spoe_appctx->frag_ctx.ctx) {
1866 ctx = spoe_appctx->frag_ctx.ctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001867 ctx->frag_ctx.spoe_appctx = NULL;
1868 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001869 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001870 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1871 }
1872
Christopher Faulet8ef75252017-02-20 22:56:03 +01001873 /* Release allocated memory */
1874 spoe_release_buffer(&spoe_appctx->buffer,
1875 &spoe_appctx->buffer_wait);
1876 pool_free2(pool2_spoe_appctx, spoe_appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001877
Christopher Fauleta1cda022016-12-21 08:58:06 +01001878 if (!LIST_ISEMPTY(&agent->applets))
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001879 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001880
Christopher Faulet8ef75252017-02-20 22:56:03 +01001881 /* If this was the last running applet, notify all waiting streams */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001882 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1883 LIST_DEL(&ctx->list);
1884 LIST_INIT(&ctx->list);
1885 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001886 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001887 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001888 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001889 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1890 LIST_DEL(&ctx->list);
1891 LIST_INIT(&ctx->list);
1892 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001893 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001894 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1895 }
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001896
1897 end:
1898 /* Update runtinme agent info */
1899 agent->frame_size = agent->max_frame_size;
1900 list_for_each_entry(spoe_appctx, &agent->applets, list)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001901 agent->frame_size = MIN(spoe_appctx->max_frame_size,
1902 agent->frame_size);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001903}
1904
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001905static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001906spoe_handle_connect_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001907{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001908 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001909 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001910 char *frame, *buf;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001911 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001912
Christopher Fauleta1cda022016-12-21 08:58:06 +01001913 if (si->state <= SI_ST_CON) {
1914 si_applet_want_put(si);
1915 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1916 goto stop;
1917 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001918 if (si->state != SI_ST_EST) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001919 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001920 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001921 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001922
Christopher Fauleta1cda022016-12-21 08:58:06 +01001923 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001924 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1925 " - Connection timed out\n",
1926 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1927 __FUNCTION__, appctx);
1928 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001929 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001930 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001931
Christopher Faulet42bfa462017-01-04 14:14:19 +01001932 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001933 SPOE_APPCTX(appctx)->task->expire =
1934 tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001935
Christopher Faulet8ef75252017-02-20 22:56:03 +01001936 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1937 * length. */
1938 buf = trash.str; frame = buf+4;
1939 ret = spoe_prepare_hahello_frame(appctx, frame,
1940 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001941 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001942 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001943
1944 switch (ret) {
1945 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001946 case 0: /* ignore => an error, cannot be ignored */
1947 goto exit;
1948
1949 case 1: /* retry later */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001950 goto stop;
1951
Christopher Faulet8ef75252017-02-20 22:56:03 +01001952 default:
1953 /* HELLO frame successfully sent, now wait for the
1954 * reply. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001955 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1956 goto next;
1957 }
1958
1959 next:
1960 return 0;
1961 stop:
1962 return 1;
1963 exit:
1964 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1965 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001966}
1967
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001968static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001969spoe_handle_connecting_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001970{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001971 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001972 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001973 char *frame;
1974 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001975
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001976
Christopher Fauletb067b062017-01-04 16:39:11 +01001977 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001978 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001979 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001980 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001981
Christopher Fauleta1cda022016-12-21 08:58:06 +01001982 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001983 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1984 " - Connection timed out\n",
1985 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1986 __FUNCTION__, appctx);
1987 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001988 goto exit;
1989 }
1990
Christopher Faulet8ef75252017-02-20 22:56:03 +01001991 frame = trash.str; trash.len = 0;
1992 ret = spoe_recv_frame(appctx, frame,
1993 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001994 if (ret > 1) {
1995 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1996 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1997 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001998 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001999 trash.len = ret + 4;
2000 ret = spoe_handle_agenthello_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002001 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002002
Christopher Fauleta1cda022016-12-21 08:58:06 +01002003 switch (ret) {
2004 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002005 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002006 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
2007 goto next;
2008
2009 case 1: /* retry later */
2010 goto stop;
2011
2012 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002013 /* HELLO handshake is finished, set the idle timeout and
2014 * add the applet in the list of running applets. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002015 agent->applets_idle++;
2016 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002017 LIST_DEL(&SPOE_APPCTX(appctx)->list);
2018 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01002019
2020 /* Update runtinme agent info */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002021 agent->frame_size = MIN(SPOE_APPCTX(appctx)->max_frame_size,
2022 agent->frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002023 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002024 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002025
Christopher Fauleta1cda022016-12-21 08:58:06 +01002026 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002027 /* Do not forget to remove processed frame from the output buffer */
2028 if (trash.len)
2029 bo_skip(si_oc(si), trash.len);
2030
2031 SPOE_APPCTX(appctx)->task->expire =
2032 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002033 return 0;
2034 stop:
2035 return 1;
2036 exit:
2037 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2038 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002039}
2040
Christopher Fauleta1cda022016-12-21 08:58:06 +01002041static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002042spoe_handle_sending_frame_appctx(struct appctx *appctx, struct spoe_context *ctx,
2043 int *skip)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002044{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002045 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
2046 char *frame, *buf;
2047 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002048
Christopher Faulet8ef75252017-02-20 22:56:03 +01002049 /* 4 bytes are reserved at the beginning of <buf> to store the frame
2050 * length. */
2051 buf = trash.str; frame = buf+4;
2052 ret = spoe_prepare_hanotify_frame(appctx, ctx, frame,
2053 SPOE_APPCTX(appctx)->max_frame_size);
2054 if (ret > 1)
2055 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002056
Christopher Faulet8ef75252017-02-20 22:56:03 +01002057 switch (ret) {
2058 case -1: /* error */
2059 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
2060 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002061
Christopher Faulet8ef75252017-02-20 22:56:03 +01002062 case 0: /* ignore */
2063 if (ctx == NULL)
2064 goto abort_frag_frame;
2065
2066 LIST_DEL(&ctx->list);
2067 LIST_INIT(&ctx->list);
2068 ctx->state = SPOE_CTX_ST_ERROR;
2069 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
2070 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2071 break;
2072
2073 case 1: /* retry */
2074 *skip = 1;
2075 break;
2076
2077 default:
2078 if (ctx == NULL)
2079 goto abort_frag_frame;
2080
2081 LIST_DEL(&ctx->list);
2082 LIST_INIT(&ctx->list);
2083 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) ||
2084 (ctx->frag_ctx.flags & SPOE_FRM_FL_FIN))
2085 goto no_frag_frame_sent;
2086 else {
2087 *skip = 1;
2088 goto frag_frame_sent;
2089 }
2090 }
2091 goto end;
2092
2093 frag_frame_sent:
2094 appctx->st0 = SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY;
2095 SPOE_APPCTX(appctx)->frag_ctx.ctx = ctx;
2096 SPOE_APPCTX(appctx)->frag_ctx.cursid = ctx->stream_id;
2097 SPOE_APPCTX(appctx)->frag_ctx.curfid = ctx->frame_id;
2098
2099 ctx->frag_ctx.spoe_appctx = SPOE_APPCTX(appctx);
2100 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
2101 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2102 goto end;
2103
2104 no_frag_frame_sent:
2105 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
2106 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2107 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
2108 }
2109 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
2110 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2111 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
2112 }
2113 else {
2114 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
2115 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
2116 }
2117 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
2118 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
2119 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
2120
2121 ctx->frag_ctx.spoe_appctx = NULL;
2122 ctx->state = SPOE_CTX_ST_WAITING_ACK;
2123 goto end;
2124
2125 abort_frag_frame:
2126 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2127 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
2128 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
2129 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
2130 goto end;
2131
2132 end:
2133 return ret;
2134}
2135
2136static int
2137spoe_handle_receiving_frame_appctx(struct appctx *appctx, int *skip)
2138{
2139 struct spoe_context *ctx = NULL;
2140 char *frame;
2141 int ret;
2142
2143 frame = trash.str; trash.len = 0;
2144 ret = spoe_recv_frame(appctx, frame,
2145 SPOE_APPCTX(appctx)->max_frame_size);
2146 if (ret > 1) {
2147 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
2148 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
2149 goto end;
2150 }
2151 trash.len = ret + 4;
2152 ret = spoe_handle_agentack_frame(appctx, &ctx, frame, ret);
2153 }
2154 switch (ret) {
2155 case -1: /* error */
2156 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
2157 break;
2158
2159 case 0: /* ignore */
2160 break;
2161
2162 case 1: /* retry */
2163 *skip = 1;
2164 break;
2165
2166 default:
2167 LIST_DEL(&ctx->list);
2168 LIST_INIT(&ctx->list);
Christopher Faulet8eda93f2017-02-09 09:44:33 +01002169
2170 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY &&
2171 ctx == SPOE_APPCTX(appctx)->frag_ctx.ctx) {
2172 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2173 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
2174 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
2175 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
2176 }
2177 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
2178 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2179
Christopher Faulet8ef75252017-02-20 22:56:03 +01002180 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2181 break;
2182 }
2183
2184 /* Do not forget to remove processed frame from the output buffer */
2185 if (trash.len)
2186 bo_skip(si_oc(appctx->owner), trash.len);
2187 end:
2188 return ret;
2189}
2190
2191static int
2192spoe_handle_processing_appctx(struct appctx *appctx)
2193{
2194 struct stream_interface *si = appctx->owner;
2195 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
2196 struct spoe_context *ctx = NULL;
2197 unsigned int fpa = 0;
2198 int ret, skip_sending = 0, skip_receiving = 0;
2199
2200 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
2201 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
2202 goto exit;
2203 }
2204
2205 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
2206 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
2207 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
2208 appctx->st1 = SPOE_APPCTX_ERR_NONE;
2209 goto next;
2210 }
2211
2212 process:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002213 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2214 " - process: fpa=%u/%u - skip_sending=%d - skip_receiving=%d"
2215 " - appctx-state=%s\n",
2216 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2217 __FUNCTION__, appctx, fpa, agent->max_fpa,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002218 skip_sending, skip_receiving,
2219 spoe_appctx_state_str[appctx->st0]);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002220
Christopher Fauleta1cda022016-12-21 08:58:06 +01002221 if (fpa > agent->max_fpa || (skip_sending && skip_receiving))
2222 goto stop;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002223 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002224 if (skip_receiving)
2225 goto stop;
2226 goto recv_frame;
2227 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002228 else if (skip_sending)
2229 goto recv_frame;
2230 else if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY) {
2231 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
2232 goto send_frame;
2233 }
2234 else if (LIST_ISEMPTY(&agent->sending_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002235 skip_sending = 1;
2236 goto recv_frame;
2237 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002238 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
Christopher Faulet4596fb72017-01-11 14:05:19 +01002239
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002240 send_frame:
Christopher Faulet4596fb72017-01-11 14:05:19 +01002241 /* Transfer the buffer ownership to the SPOE appctx */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002242 if (ctx) {
2243 SPOE_APPCTX(appctx)->buffer = ctx->buffer;
2244 ctx->buffer = &buf_empty;
2245 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01002246 ret = spoe_handle_sending_frame_appctx(appctx, ctx, &skip_sending);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002247 switch (ret) {
2248 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002249 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002250
Christopher Fauleta1cda022016-12-21 08:58:06 +01002251 case 0: /* ignore */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002252 spoe_release_buffer(&SPOE_APPCTX(appctx)->buffer,
2253 &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002254 agent->sending_rate++;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002255 fpa++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002256 break;
2257
Christopher Fauleta1cda022016-12-21 08:58:06 +01002258 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002259 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002260
Christopher Fauleta1cda022016-12-21 08:58:06 +01002261 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002262 spoe_release_buffer(&SPOE_APPCTX(appctx)->buffer,
2263 &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002264 agent->sending_rate++;
2265 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002266 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002267 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002268 if (fpa > agent->max_fpa)
2269 goto stop;
2270
2271 recv_frame:
2272 if (skip_receiving)
2273 goto process;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002274 ret = spoe_handle_receiving_frame_appctx(appctx, &skip_receiving);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002275 switch (ret) {
2276 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002277 goto next;
2278
2279 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002280 fpa++;
2281 break;
2282
2283 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002284 break;
2285
2286 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01002287 fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002288 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002289 }
2290 goto process;
2291
2292 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002293 SPOE_APPCTX(appctx)->task->expire =
2294 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002295 return 0;
2296 stop:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002297 if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002298 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002299 agent->applets_idle++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002300 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01002301 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
2302 LIST_DEL(&SPOE_APPCTX(appctx)->list);
2303 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002304 if (fpa)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002305 SPOE_APPCTX(appctx)->task->expire =
2306 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002307 }
2308 return 1;
2309
2310 exit:
2311 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2312 return 0;
2313}
2314
2315static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002316spoe_handle_disconnect_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002317{
2318 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002319 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002320 char *frame, *buf;
2321 int ret;
Christopher Fauletb067b062017-01-04 16:39:11 +01002322
Christopher Fauleta1cda022016-12-21 08:58:06 +01002323 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
2324 goto exit;
2325
2326 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
2327 goto exit;
2328
Christopher Faulet8ef75252017-02-20 22:56:03 +01002329 /* 4 bytes are reserved at the beginning of <buf> to store the frame
2330 * length. */
2331 buf = trash.str; frame = buf+4;
2332 ret = spoe_prepare_hadiscon_frame(appctx, frame,
2333 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002334 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002335 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002336
2337 switch (ret) {
2338 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002339 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002340 goto exit;
2341
2342 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002343 goto stop;
2344
2345 default:
2346 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2347 " - disconnected by HAProxy (%d): %s\n",
2348 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002349 __FUNCTION__, appctx,
2350 SPOE_APPCTX(appctx)->status_code,
2351 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002352
2353 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
2354 goto next;
2355 }
2356
2357 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002358 SPOE_APPCTX(appctx)->task->expire =
2359 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002360 return 0;
2361 stop:
2362 return 1;
2363 exit:
2364 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2365 return 0;
2366}
2367
2368static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002369spoe_handle_disconnecting_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002370{
2371 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002372 char *frame;
2373 int ret;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002374
Christopher Fauletb067b062017-01-04 16:39:11 +01002375 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002376 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002377 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002378 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002379
Christopher Fauletb067b062017-01-04 16:39:11 +01002380 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002381 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002382 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002383 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002384
Christopher Faulet8ef75252017-02-20 22:56:03 +01002385 frame = trash.str; trash.len = 0;
2386 ret = spoe_recv_frame(appctx, frame,
2387 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002388 if (ret > 1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002389 trash.len = ret + 4;
2390 ret = spoe_handle_agentdiscon_frame(appctx, frame, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002391 }
2392
2393 switch (ret) {
2394 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002395 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2396 " - error on frame (%s)\n",
2397 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002398 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01002399 __FUNCTION__, appctx,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002400 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002401 goto exit;
2402
2403 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002404 goto next;
2405
2406 case 1: /* retry */
2407 goto stop;
2408
2409 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01002410 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002411 " - disconnected by peer (%d): %.*s\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002412 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002413 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002414 __FUNCTION__, appctx, SPOE_APPCTX(appctx)->status_code,
2415 SPOE_APPCTX(appctx)->rlen, SPOE_APPCTX(appctx)->reason);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002416 goto exit;
2417 }
2418
2419 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002420 /* Do not forget to remove processed frame from the output buffer */
2421 if (trash.len)
2422 bo_skip(si_oc(appctx->owner), trash.len);
2423
Christopher Fauleta1cda022016-12-21 08:58:06 +01002424 return 0;
2425 stop:
2426 return 1;
2427 exit:
2428 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2429 return 0;
2430}
2431
2432/* I/O Handler processing messages exchanged with the agent */
2433static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002434spoe_handle_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002435{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002436 struct stream_interface *si = appctx->owner;
2437 struct spoe_agent *agent;
2438
2439 if (SPOE_APPCTX(appctx) == NULL)
2440 return;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002441
Christopher Faulet8ef75252017-02-20 22:56:03 +01002442 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
2443 agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletb067b062017-01-04 16:39:11 +01002444
Christopher Fauleta1cda022016-12-21 08:58:06 +01002445 switchstate:
2446 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2447 " - appctx-state=%s\n",
2448 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2449 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
2450
2451 switch (appctx->st0) {
2452 case SPOE_APPCTX_ST_CONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002453 if (spoe_handle_connect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002454 goto out;
2455 goto switchstate;
2456
2457 case SPOE_APPCTX_ST_CONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002458 if (spoe_handle_connecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002459 goto out;
2460 goto switchstate;
2461
2462 case SPOE_APPCTX_ST_IDLE:
2463 if (stopping &&
2464 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01002465 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002466 SPOE_APPCTX(appctx)->task->expire =
2467 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002468 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002469 goto switchstate;
2470 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002471 agent->applets_idle--;
2472 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2473 /* fall through */
2474
2475 case SPOE_APPCTX_ST_PROCESSING:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002476 case SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY:
2477 case SPOE_APPCTX_ST_WAITING_SYNC_ACK:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002478 if (spoe_handle_processing_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002479 goto out;
2480 goto switchstate;
2481
2482 case SPOE_APPCTX_ST_DISCONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002483 if (spoe_handle_disconnect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002484 goto out;
2485 goto switchstate;
2486
2487 case SPOE_APPCTX_ST_DISCONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002488 if (spoe_handle_disconnecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002489 goto out;
2490 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002491
2492 case SPOE_APPCTX_ST_EXIT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01002493 appctx->st0 = SPOE_APPCTX_ST_END;
2494 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
2495
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002496 si_shutw(si);
2497 si_shutr(si);
2498 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002499 /* fall through */
2500
2501 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002502 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002503 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002504 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002505 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
2506 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002507 si_oc(si)->flags |= CF_READ_DONTWAIT;
2508 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002509}
2510
2511struct applet spoe_applet = {
2512 .obj_type = OBJ_TYPE_APPLET,
2513 .name = "<SPOE>", /* used for logging */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002514 .fct = spoe_handle_appctx,
2515 .release = spoe_release_appctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002516};
2517
2518/* Create a SPOE applet. On success, the created applet is returned, else
2519 * NULL. */
2520static struct appctx *
Christopher Faulet8ef75252017-02-20 22:56:03 +01002521spoe_create_appctx(struct spoe_config *conf)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002522{
2523 struct appctx *appctx;
2524 struct session *sess;
2525 struct task *task;
2526 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002527
2528 if ((appctx = appctx_new(&spoe_applet)) == NULL)
2529 goto out_error;
2530
Christopher Faulet42bfa462017-01-04 14:14:19 +01002531 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
2532 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002533 goto out_free_appctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002534 memset(appctx->ctx.spoe.ptr, 0, pool2_spoe_appctx->size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002535
Christopher Faulet42bfa462017-01-04 14:14:19 +01002536 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
2537 if ((SPOE_APPCTX(appctx)->task = task_new()) == NULL)
2538 goto out_free_spoe_appctx;
2539
2540 SPOE_APPCTX(appctx)->owner = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002541 SPOE_APPCTX(appctx)->task->process = spoe_process_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002542 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
2543 SPOE_APPCTX(appctx)->task->context = appctx;
2544 SPOE_APPCTX(appctx)->agent = conf->agent;
2545 SPOE_APPCTX(appctx)->version = 0;
2546 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
2547 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01002548 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet4596fb72017-01-11 14:05:19 +01002549 SPOE_APPCTX(appctx)->buffer = &buf_empty;
2550
2551 LIST_INIT(&SPOE_APPCTX(appctx)->buffer_wait.list);
2552 SPOE_APPCTX(appctx)->buffer_wait.target = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002553 SPOE_APPCTX(appctx)->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002554
2555 LIST_INIT(&SPOE_APPCTX(appctx)->list);
2556 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002557
Willy Tarreau5820a362016-12-22 15:59:02 +01002558 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002559 if (!sess)
2560 goto out_free_spoe;
2561
2562 if ((task = task_new()) == NULL)
2563 goto out_free_sess;
2564
2565 if ((strm = stream_new(sess, task, &appctx->obj_type)) == NULL)
2566 goto out_free_task;
2567
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002568 stream_set_backend(strm, conf->agent->b.be);
2569
2570 /* applet is waiting for data */
2571 si_applet_cant_get(&strm->si[0]);
2572 appctx_wakeup(appctx);
2573
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002574 strm->do_log = NULL;
2575 strm->res.flags |= CF_READ_DONTWAIT;
2576
2577 conf->agent_fe.feconn++;
2578 jobs++;
2579 totalconn++;
2580
Christopher Faulet42bfa462017-01-04 14:14:19 +01002581 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
2582 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002583 conf->agent->applets_act++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002584 return appctx;
2585
2586 /* Error unrolling */
2587 out_free_task:
2588 task_free(task);
2589 out_free_sess:
2590 session_free(sess);
2591 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002592 task_free(SPOE_APPCTX(appctx)->task);
2593 out_free_spoe_appctx:
2594 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002595 out_free_appctx:
2596 appctx_free(appctx);
2597 out_error:
2598 return NULL;
2599}
2600
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002601static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002602spoe_queue_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002603{
2604 struct spoe_config *conf = FLT_CONF(ctx->filter);
2605 struct spoe_agent *agent = conf->agent;
2606 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002607 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002608 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002609
Christopher Fauleta1cda022016-12-21 08:58:06 +01002610 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002611
Christopher Fauleta1cda022016-12-21 08:58:06 +01002612 /* Check if we need to create a new SPOE applet or not. */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002613 if (agent->applets_act >= min_applets &&
2614 agent->applets_idle &&
2615 agent->sending_rate)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002616 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002617
2618 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002619 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002620 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2621 ctx->strm);
2622
Christopher Fauleta1cda022016-12-21 08:58:06 +01002623 /* Do not try to create a new applet if there is no server up for the
2624 * agent's backend. */
2625 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2626 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2627 " - cannot create SPOE appctx: no server up\n",
2628 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2629 __FUNCTION__, ctx->strm);
2630 goto end;
2631 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002632
Christopher Fauleta1cda022016-12-21 08:58:06 +01002633 /* Do not try to create a new applet if we have reached the maximum of
2634 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002635 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002636 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2637 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2638 " - cannot create SPOE appctx: max CPS reached\n",
2639 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2640 __FUNCTION__, ctx->strm);
2641 goto end;
2642 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002643 }
2644
Christopher Faulet8ef75252017-02-20 22:56:03 +01002645 appctx = spoe_create_appctx(conf);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002646 if (appctx == NULL) {
2647 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2648 " - failed to create SPOE appctx\n",
2649 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2650 __FUNCTION__, ctx->strm);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002651 send_log(ctx->strm->be, LOG_EMERG,
2652 "SPOE: [%s] failed to create SPOE applet\n",
2653 agent->id);
2654
Christopher Fauleta1cda022016-12-21 08:58:06 +01002655 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002656 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002657 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002658 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002659
Christopher Fauleta1cda022016-12-21 08:58:06 +01002660 /* Increase the per-process number of cumulated connections */
2661 if (agent->cps_max > 0)
2662 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002663
Christopher Fauleta1cda022016-12-21 08:58:06 +01002664 end:
2665 /* The only reason to return an error is when there is no applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002666 if (LIST_ISEMPTY(&agent->applets)) {
2667 ctx->status_code = SPOE_CTX_ERR_RES;
2668 return -1;
2669 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002670
Christopher Fauleta1cda022016-12-21 08:58:06 +01002671 /* Add the SPOE context in the sending queue and update all running
2672 * info */
2673 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2674 if (agent->sending_rate)
2675 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002676
2677 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002678 " - Add stream in sending queue"
2679 " - applets_act=%u - applets_idle=%u - sending_rate=%u\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002680 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002681 ctx->strm, agent->applets_act, agent->applets_idle,
2682 agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002683
Christopher Fauleta1cda022016-12-21 08:58:06 +01002684 /* Finally try to wakeup the first IDLE applet found and move it at the
2685 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002686 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2687 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002688 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002689 spoe_wakeup_appctx(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002690 LIST_DEL(&spoe_appctx->list);
2691 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002692 break;
2693 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002694 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002695 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002696}
2697
2698/***************************************************************************
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002699 * Functions that encode SPOE messages
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002700 **************************************************************************/
Christopher Faulet8ef75252017-02-20 22:56:03 +01002701/* Encode SPOE messages for a specific event. Info in <ctx->frag_ctx>, if any,
2702 * are used to handle fragmented content. On success it returns 1. If an error
2703 * occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002704static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002705spoe_encode_messages(struct stream *s, struct spoe_context *ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002706 struct list *messages, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002707{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002708 struct spoe_config *conf = FLT_CONF(ctx->filter);
2709 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002710 struct spoe_message *msg;
2711 struct sample *smp;
2712 struct spoe_arg *arg;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002713 char *p, *end;
2714 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002715
Christopher Faulet8ef75252017-02-20 22:56:03 +01002716 p = ctx->buffer->p;
2717 end = p + agent->frame_size - FRAME_HDR_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002718
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002719 /* Resume encoding of a SPOE message */
2720 if (ctx->frag_ctx.curmsg != NULL) {
2721 msg = ctx->frag_ctx.curmsg;
2722 goto encode_message;
2723 }
2724
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002725 /* Loop on messages */
2726 list_for_each_entry(msg, messages, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002727 ctx->frag_ctx.curmsg = msg;
2728 ctx->frag_ctx.curarg = NULL;
2729 ctx->frag_ctx.curoff = UINT_MAX;
2730
2731 encode_message:
2732 /* Resume encoding of a SPOE argument */
2733 if (ctx->frag_ctx.curarg != NULL) {
2734 arg = ctx->frag_ctx.curarg;
2735 goto encode_argument;
2736 }
2737
2738 if (ctx->frag_ctx.curoff != UINT_MAX)
2739 goto encode_msg_payload;
2740
Christopher Faulet8ef75252017-02-20 22:56:03 +01002741 /* Check if there is enough space for the message name and the
2742 * number of arguments. It implies <msg->id_len> is encoded on 2
2743 * bytes, at most (< 2288). */
2744 if (p + 2 + msg->id_len + 1 > end)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002745 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002746
Christopher Faulet8ef75252017-02-20 22:56:03 +01002747 /* Encode the message name */
2748 if (spoe_encode_buffer(msg->id, msg->id_len, &p, end) == -1)
2749 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002750
Christopher Faulet8ef75252017-02-20 22:56:03 +01002751 /* Set the number of arguments for this message */
2752 *p++ = msg->nargs;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002753
2754 ctx->frag_ctx.curoff = 0;
2755 encode_msg_payload:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002756
2757 /* Loop on arguments */
2758 list_for_each_entry(arg, &msg->args, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002759 ctx->frag_ctx.curarg = arg;
2760 ctx->frag_ctx.curoff = UINT_MAX;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002761
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002762 encode_argument:
2763 if (ctx->frag_ctx.curoff != UINT_MAX)
2764 goto encode_arg_value;
2765
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002766 /* Encode the arguement name as a string. It can by NULL */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002767 if (spoe_encode_buffer(arg->name, arg->name_len, &p, end) == -1)
2768 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002769
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002770 ctx->frag_ctx.curoff = 0;
2771 encode_arg_value:
2772
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002773 /* Fetch the arguement value */
Christopher Faulet8ef75252017-02-20 22:56:03 +01002774 smp = sample_process(s->be, s->sess, s,
2775 dir|SMP_OPT_FINAL, arg->expr, NULL);
2776 ret = spoe_encode_data(smp, &ctx->frag_ctx.curoff, &p, end);
2777 if (ret == -1 || ctx->frag_ctx.curoff)
2778 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002779 }
2780 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002781
2782 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002783 " - encode %s messages - spoe_appctx=%p"
2784 "- max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002785 (int)now.tv_sec, (int)now.tv_usec,
2786 agent->id, __FUNCTION__, s,
2787 ((ctx->flags & SPOE_CTX_FL_FRAGMENTED) ? "last fragment of" : "unfragmented"),
Christopher Faulet8ef75252017-02-20 22:56:03 +01002788 ctx->frag_ctx.spoe_appctx, (agent->frame_size - FRAME_HDR_SIZE),
2789 p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002790
Christopher Faulet8ef75252017-02-20 22:56:03 +01002791 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002792 ctx->frag_ctx.curmsg = NULL;
2793 ctx->frag_ctx.curarg = NULL;
2794 ctx->frag_ctx.curoff = 0;
2795 ctx->frag_ctx.flags = SPOE_FRM_FL_FIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002796 return 1;
2797
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002798 too_big:
2799 // FIXME: if fragmentation not supported =>
2800 // ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
2801 // return -1;
2802
2803 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002804 " - encode fragmented messages - spoe_appctx=%p"
2805 " - curmsg=%p - curarg=%p - curoff=%u"
2806 " - max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002807 (int)now.tv_sec, (int)now.tv_usec,
2808 agent->id, __FUNCTION__, s, ctx->frag_ctx.spoe_appctx,
2809 ctx->frag_ctx.curmsg, ctx->frag_ctx.curarg, ctx->frag_ctx.curoff,
Christopher Faulet8ef75252017-02-20 22:56:03 +01002810 (agent->frame_size - FRAME_HDR_SIZE), p - ctx->buffer->p);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002811
Christopher Faulet8ef75252017-02-20 22:56:03 +01002812 ctx->buffer->i = p - ctx->buffer->p;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002813 ctx->flags |= SPOE_CTX_FL_FRAGMENTED;
2814 ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN;
2815 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002816}
2817
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002818
2819/***************************************************************************
2820 * Functions that handle SPOE actions
2821 **************************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002822/* Helper function to set a variable */
2823static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002824spoe_set_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002825 struct sample *smp)
2826{
2827 struct spoe_config *conf = FLT_CONF(ctx->filter);
2828 struct spoe_agent *agent = conf->agent;
2829 char varname[64];
2830
2831 memset(varname, 0, sizeof(varname));
2832 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2833 scope, agent->var_pfx, len, name);
2834 vars_set_by_name_ifexist(varname, len, smp);
2835}
2836
2837/* Helper function to unset a variable */
2838static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002839spoe_unset_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002840 struct sample *smp)
2841{
2842 struct spoe_config *conf = FLT_CONF(ctx->filter);
2843 struct spoe_agent *agent = conf->agent;
2844 char varname[64];
2845
2846 memset(varname, 0, sizeof(varname));
2847 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2848 scope, agent->var_pfx, len, name);
2849 vars_unset_by_name_ifexist(varname, len, smp);
2850}
2851
2852
Christopher Faulet8ef75252017-02-20 22:56:03 +01002853static inline int
2854spoe_decode_action_set_var(struct stream *s, struct spoe_context *ctx,
2855 char **buf, char *end, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002856{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002857 char *str, *scope, *p = *buf;
2858 struct sample smp;
2859 uint64_t sz;
2860 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002861
Christopher Faulet8ef75252017-02-20 22:56:03 +01002862 if (p + 2 >= end)
2863 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002864
Christopher Faulet8ef75252017-02-20 22:56:03 +01002865 /* SET-VAR requires 3 arguments */
2866 if (*p++ != 3)
2867 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002868
Christopher Faulet8ef75252017-02-20 22:56:03 +01002869 switch (*p++) {
2870 case SPOE_SCOPE_PROC: scope = "proc"; break;
2871 case SPOE_SCOPE_SESS: scope = "sess"; break;
2872 case SPOE_SCOPE_TXN : scope = "txn"; break;
2873 case SPOE_SCOPE_REQ : scope = "req"; break;
2874 case SPOE_SCOPE_RES : scope = "res"; break;
2875 default: goto skip;
2876 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002877
Christopher Faulet8ef75252017-02-20 22:56:03 +01002878 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2879 goto skip;
2880 memset(&smp, 0, sizeof(smp));
2881 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002882
Christopher Faulet8ef75252017-02-20 22:56:03 +01002883 if (spoe_decode_data(&p, end, &smp) == -1)
2884 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002885
Christopher Faulet8ef75252017-02-20 22:56:03 +01002886 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2887 " - set-var '%s.%s.%.*s'\n",
2888 (int)now.tv_sec, (int)now.tv_usec,
2889 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2890 __FUNCTION__, s, scope,
2891 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2892 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002893
Christopher Faulet8ef75252017-02-20 22:56:03 +01002894 spoe_set_var(ctx, scope, str, sz, &smp);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002895
Christopher Faulet8ef75252017-02-20 22:56:03 +01002896 ret = (p - *buf);
2897 *buf = p;
2898 return ret;
2899 skip:
2900 return 0;
2901}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002902
Christopher Faulet8ef75252017-02-20 22:56:03 +01002903static inline int
2904spoe_decode_action_unset_var(struct stream *s, struct spoe_context *ctx,
2905 char **buf, char *end, int dir)
2906{
2907 char *str, *scope, *p = *buf;
2908 struct sample smp;
2909 uint64_t sz;
2910 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002911
Christopher Faulet8ef75252017-02-20 22:56:03 +01002912 if (p + 2 >= end)
2913 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002914
Christopher Faulet8ef75252017-02-20 22:56:03 +01002915 /* UNSET-VAR requires 2 arguments */
2916 if (*p++ != 2)
2917 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002918
Christopher Faulet8ef75252017-02-20 22:56:03 +01002919 switch (*p++) {
2920 case SPOE_SCOPE_PROC: scope = "proc"; break;
2921 case SPOE_SCOPE_SESS: scope = "sess"; break;
2922 case SPOE_SCOPE_TXN : scope = "txn"; break;
2923 case SPOE_SCOPE_REQ : scope = "req"; break;
2924 case SPOE_SCOPE_RES : scope = "res"; break;
2925 default: goto skip;
2926 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002927
Christopher Faulet8ef75252017-02-20 22:56:03 +01002928 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2929 goto skip;
2930 memset(&smp, 0, sizeof(smp));
2931 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002932
Christopher Faulet8ef75252017-02-20 22:56:03 +01002933 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2934 " - unset-var '%s.%s.%.*s'\n",
2935 (int)now.tv_sec, (int)now.tv_usec,
2936 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2937 __FUNCTION__, s, scope,
2938 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2939 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002940
Christopher Faulet8ef75252017-02-20 22:56:03 +01002941 spoe_unset_var(ctx, scope, str, sz, &smp);
2942
2943 ret = (p - *buf);
2944 *buf = p;
2945 return ret;
2946 skip:
2947 return 0;
2948}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002949
Christopher Faulet8ef75252017-02-20 22:56:03 +01002950/* Process SPOE actions for a specific event. It returns 1 on success. If an
2951 * error occurred, 0 is returned. */
2952static int
2953spoe_process_actions(struct stream *s, struct spoe_context *ctx,
2954 enum spoe_event ev, int dir)
2955{
2956 char *p, *end;
2957 int ret;
2958
2959 p = ctx->buffer->p;
2960 end = p + ctx->buffer->i;
2961
2962 while (p < end) {
2963 enum spoe_action_type type;
2964
2965 type = *p++;
2966 switch (type) {
2967 case SPOE_ACT_T_SET_VAR:
2968 ret = spoe_decode_action_set_var(s, ctx, &p, end, dir);
2969 if (!ret)
2970 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002971 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002972
Christopher Faulet8ef75252017-02-20 22:56:03 +01002973 case SPOE_ACT_T_UNSET_VAR:
2974 ret = spoe_decode_action_unset_var(s, ctx, &p, end, dir);
2975 if (!ret)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002976 goto skip;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002977 break;
2978
2979 default:
2980 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002981 }
2982 }
2983
2984 return 1;
2985 skip:
2986 return 0;
2987}
2988
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002989/***************************************************************************
2990 * Functions that process SPOE events
2991 **************************************************************************/
2992static inline int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002993spoe_start_event_processing(struct spoe_context *ctx, int dir)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002994{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002995 /* If a process is already started for this SPOE context, retry
2996 * later. */
2997 if (ctx->flags & SPOE_CTX_FL_PROCESS)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002998 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002999
3000 /* Set the right flag to prevent request and response processing
3001 * in same time. */
3002 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
3003 ? SPOE_CTX_FL_REQ_PROCESS
3004 : SPOE_CTX_FL_RSP_PROCESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003005 return 1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003006}
3007
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003008static inline void
Christopher Faulet8ef75252017-02-20 22:56:03 +01003009spoe_stop_event_processing(struct spoe_context *ctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003010{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003011 struct spoe_appctx *sa = ctx->frag_ctx.spoe_appctx;
3012
3013 if (sa) {
3014 sa->frag_ctx.ctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003015 spoe_wakeup_appctx(sa->owner);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003016 }
3017
Christopher Fauleta1cda022016-12-21 08:58:06 +01003018 /* Reset the flag to allow next processing */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003019 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003020
Christopher Fauletb067b062017-01-04 16:39:11 +01003021 ctx->status_code = 0;
3022
Christopher Fauleta1cda022016-12-21 08:58:06 +01003023 /* Reset processing timer */
3024 ctx->process_exp = TICK_ETERNITY;
3025
Christopher Faulet8ef75252017-02-20 22:56:03 +01003026 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003027
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003028 ctx->frag_ctx.spoe_appctx = NULL;
3029 ctx->frag_ctx.curmsg = NULL;
3030 ctx->frag_ctx.curarg = NULL;
3031 ctx->frag_ctx.curoff = 0;
3032 ctx->frag_ctx.flags = 0;
3033
Christopher Fauleta1cda022016-12-21 08:58:06 +01003034 if (!LIST_ISEMPTY(&ctx->list)) {
3035 LIST_DEL(&ctx->list);
3036 LIST_INIT(&ctx->list);
3037 }
3038}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003039
3040/* Process a SPOE event. First, this functions will process messages attached to
3041 * this event and send them to an agent in a NOTIFY frame. Then, it will wait a
3042 * ACK frame to process corresponding actions. During all the processing, it
3043 * returns 0 and it returns 1 when the processing is finished. If an error
3044 * occurred, -1 is returned. */
3045static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01003046spoe_process_event(struct stream *s, struct spoe_context *ctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003047 enum spoe_event ev)
3048{
Christopher Fauletf7a30922016-11-10 15:04:51 +01003049 struct spoe_config *conf = FLT_CONF(ctx->filter);
3050 struct spoe_agent *agent = conf->agent;
3051 int dir, ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003052
3053 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3054 " - ctx-state=%s - event=%s\n",
3055 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletf7a30922016-11-10 15:04:51 +01003056 agent->id, __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003057 spoe_event_str[ev]);
3058
3059 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
3060
3061 if (LIST_ISEMPTY(&(ctx->messages[ev])))
3062 goto out;
3063
3064 if (ctx->state == SPOE_CTX_ST_ERROR)
3065 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003066
3067 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
3068 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3069 " - failed to process event '%s': timeout\n",
3070 (int)now.tv_sec, (int)now.tv_usec,
3071 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
Christopher Fauletb067b062017-01-04 16:39:11 +01003072 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003073 goto error;
3074 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003075
3076 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01003077 if (agent->eps_max > 0) {
3078 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
3079 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3080 " - skip event '%s': max EPS reached\n",
3081 (int)now.tv_sec, (int)now.tv_usec,
3082 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
3083 goto skip;
3084 }
3085 }
3086
Christopher Fauletf7a30922016-11-10 15:04:51 +01003087 if (!tick_isset(ctx->process_exp)) {
3088 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
3089 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
3090 ctx->process_exp);
3091 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01003092 ret = spoe_start_event_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01003093 if (!ret)
3094 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003095
Christopher Faulet8ef75252017-02-20 22:56:03 +01003096 if (spoe_queue_context(ctx) < 0)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003097 goto error;
3098
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003099 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003100 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003101 }
3102
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003103 if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003104 if (!spoe_acquire_buffer(&ctx->buffer, &ctx->buffer_wait))
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003105 goto out;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003106 ret = spoe_encode_messages(s, ctx, &(ctx->messages[ev]), dir);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003107 if (ret < 0)
3108 goto error;
3109 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
3110 }
3111
3112 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
3113 if (ctx->frag_ctx.spoe_appctx)
Christopher Faulet8ef75252017-02-20 22:56:03 +01003114 spoe_wakeup_appctx(ctx->frag_ctx.spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003115 ret = 0;
3116 goto out;
3117 }
3118
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003119 if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
3120 ret = 0;
3121 goto out;
3122 }
3123
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003124 if (ctx->state == SPOE_CTX_ST_DONE) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003125 spoe_process_actions(s, ctx, ev, dir);
3126 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003127 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003128 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003129 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003130 }
3131
3132 out:
3133 return ret;
3134
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003135 error:
Christopher Faulet48026722016-11-16 15:01:12 +01003136 if (agent->eps_max > 0)
3137 update_freq_ctr(&agent->err_per_sec, 1);
3138
Christopher Faulet985532d2016-11-16 15:36:19 +01003139 if (agent->var_on_error) {
3140 struct sample smp;
3141
3142 memset(&smp, 0, sizeof(smp));
3143 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb067b062017-01-04 16:39:11 +01003144 smp.data.u.sint = ctx->status_code;
Christopher Faulet985532d2016-11-16 15:36:19 +01003145 smp.data.type = SMP_T_BOOL;
3146
Christopher Faulet8ef75252017-02-20 22:56:03 +01003147 spoe_set_var(ctx, "txn", agent->var_on_error,
Christopher Faulet985532d2016-11-16 15:36:19 +01003148 strlen(agent->var_on_error), &smp);
3149 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003150 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3151 " - failed to create process event '%s': code=%u\n",
3152 (int)now.tv_sec, (int)now.tv_usec, agent->id,
3153 __FUNCTION__, ctx->strm, spoe_event_str[ev],
3154 ctx->status_code);
Christopher Faulet72bcc472017-01-04 16:39:41 +01003155 send_log(ctx->strm->be, LOG_WARNING,
3156 "SPOE: [%s] failed to process event '%s': code=%u\n",
3157 agent->id, spoe_event_str[ev], ctx->status_code);
Christopher Faulet985532d2016-11-16 15:36:19 +01003158
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003159 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
3160 ? SPOE_CTX_ST_READY
Christopher Fauletb067b062017-01-04 16:39:11 +01003161 : SPOE_CTX_ST_NONE);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003162 ret = 1;
3163 goto end;
3164
3165 skip:
3166 ctx->state = SPOE_CTX_ST_READY;
3167 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003168
Christopher Fauleta1cda022016-12-21 08:58:06 +01003169 end:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003170 spoe_stop_event_processing(ctx);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003171 return ret;
3172}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003173
3174/***************************************************************************
3175 * Functions that create/destroy SPOE contexts
3176 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01003177static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01003178spoe_acquire_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003179{
Christopher Faulet4596fb72017-01-11 14:05:19 +01003180 if (*buf != &buf_empty)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003181 return 1;
3182
Christopher Faulet4596fb72017-01-11 14:05:19 +01003183 if (!LIST_ISEMPTY(&buffer_wait->list)) {
3184 LIST_DEL(&buffer_wait->list);
3185 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003186 }
3187
Christopher Faulet4596fb72017-01-11 14:05:19 +01003188 if (b_alloc_margin(buf, global.tune.reserved_bufs))
Christopher Fauleta1cda022016-12-21 08:58:06 +01003189 return 1;
3190
Christopher Faulet4596fb72017-01-11 14:05:19 +01003191 LIST_ADDQ(&buffer_wq, &buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003192 return 0;
3193}
3194
3195static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01003196spoe_release_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003197{
Christopher Faulet4596fb72017-01-11 14:05:19 +01003198 if (!LIST_ISEMPTY(&buffer_wait->list)) {
3199 LIST_DEL(&buffer_wait->list);
3200 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003201 }
3202
3203 /* Release the buffer if needed */
Christopher Faulet4596fb72017-01-11 14:05:19 +01003204 if (*buf != &buf_empty) {
3205 b_free(buf);
3206 offer_buffers(buffer_wait->target,
3207 tasks_run_queue + applets_active_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003208 }
3209}
3210
Christopher Faulet4596fb72017-01-11 14:05:19 +01003211static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01003212spoe_wakeup_context(struct spoe_context *ctx)
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003213{
3214 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
3215 return 1;
3216}
3217
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003218static struct spoe_context *
Christopher Faulet8ef75252017-02-20 22:56:03 +01003219spoe_create_context(struct filter *filter)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003220{
3221 struct spoe_config *conf = FLT_CONF(filter);
3222 struct spoe_context *ctx;
3223
3224 ctx = pool_alloc_dirty(pool2_spoe_ctx);
3225 if (ctx == NULL) {
3226 return NULL;
3227 }
3228 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01003229 ctx->filter = filter;
3230 ctx->state = SPOE_CTX_ST_NONE;
3231 ctx->status_code = SPOE_CTX_ERR_NONE;
3232 ctx->flags = 0;
3233 ctx->messages = conf->agent->messages;
3234 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003235 LIST_INIT(&ctx->buffer_wait.list);
3236 ctx->buffer_wait.target = ctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003237 ctx->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003238 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003239
Christopher Fauletf7a30922016-11-10 15:04:51 +01003240 ctx->stream_id = 0;
3241 ctx->frame_id = 1;
3242 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003243
3244 return ctx;
3245}
3246
3247static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01003248spoe_destroy_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003249{
3250 if (!ctx)
3251 return;
3252
Christopher Faulet8ef75252017-02-20 22:56:03 +01003253 spoe_stop_event_processing(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003254 pool_free2(pool2_spoe_ctx, ctx);
3255}
3256
3257static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01003258spoe_reset_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003259{
3260 ctx->state = SPOE_CTX_ST_READY;
3261 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
3262}
3263
3264
3265/***************************************************************************
3266 * Hooks that manage the filter lifecycle (init/check/deinit)
3267 **************************************************************************/
3268/* Signal handler: Do a soft stop, wakeup SPOE applet */
3269static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01003270spoe_sig_stop(struct sig_handler *sh)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003271{
3272 struct proxy *p;
3273
3274 p = proxy;
3275 while (p) {
3276 struct flt_conf *fconf;
3277
3278 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01003279 struct spoe_config *conf;
3280 struct spoe_agent *agent;
Christopher Faulet42bfa462017-01-04 14:14:19 +01003281 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003282
Christopher Faulet3b386a32017-02-23 10:17:15 +01003283 if (fconf->id != spoe_filter_id)
3284 continue;
3285
3286 conf = fconf->conf;
3287 agent = conf->agent;
3288
Christopher Faulet42bfa462017-01-04 14:14:19 +01003289 list_for_each_entry(spoe_appctx, &agent->applets, list) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003290 spoe_wakeup_appctx(spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003291 }
3292 }
3293 p = p->next;
3294 }
3295}
3296
3297
3298/* Initialize the SPOE filter. Returns -1 on error, else 0. */
3299static int
3300spoe_init(struct proxy *px, struct flt_conf *fconf)
3301{
3302 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003303
3304 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
3305 init_new_proxy(&conf->agent_fe);
3306 conf->agent_fe.parent = conf->agent;
3307 conf->agent_fe.last_change = now.tv_sec;
3308 conf->agent_fe.id = conf->agent->id;
3309 conf->agent_fe.cap = PR_CAP_FE;
3310 conf->agent_fe.mode = PR_MODE_TCP;
3311 conf->agent_fe.maxconn = 0;
3312 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
3313 conf->agent_fe.conn_retries = CONN_RETRIES;
3314 conf->agent_fe.accept = frontend_accept;
3315 conf->agent_fe.srv = NULL;
3316 conf->agent_fe.timeout.client = TICK_ETERNITY;
3317 conf->agent_fe.default_target = &spoe_applet.obj_type;
3318 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
3319
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003320 if (!sighandler_registered) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003321 signal_register_fct(0, spoe_sig_stop, 0);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003322 sighandler_registered = 1;
3323 }
3324
3325 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003326}
3327
3328/* Free ressources allocated by the SPOE filter. */
3329static void
3330spoe_deinit(struct proxy *px, struct flt_conf *fconf)
3331{
3332 struct spoe_config *conf = fconf->conf;
3333
3334 if (conf) {
3335 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003336
Christopher Faulet8ef75252017-02-20 22:56:03 +01003337 spoe_release_agent(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003338 free(conf);
3339 }
3340 fconf->conf = NULL;
3341}
3342
3343/* Check configuration of a SPOE filter for a specified proxy.
3344 * Return 1 on error, else 0. */
3345static int
3346spoe_check(struct proxy *px, struct flt_conf *fconf)
3347{
3348 struct spoe_config *conf = fconf->conf;
3349 struct proxy *target;
3350
3351 target = proxy_be_by_name(conf->agent->b.name);
3352 if (target == NULL) {
3353 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
3354 " declared at %s:%d.\n",
3355 px->id, conf->agent->b.name, conf->agent->id,
3356 conf->agent->conf.file, conf->agent->conf.line);
3357 return 1;
3358 }
3359 if (target->mode != PR_MODE_TCP) {
3360 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
3361 " at %s:%d does not support HTTP mode.\n",
3362 px->id, target->id, conf->agent->id,
3363 conf->agent->conf.file, conf->agent->conf.line);
3364 return 1;
3365 }
3366
3367 free(conf->agent->b.name);
3368 conf->agent->b.name = NULL;
3369 conf->agent->b.be = target;
3370 return 0;
3371}
3372
3373/**************************************************************************
3374 * Hooks attached to a stream
3375 *************************************************************************/
3376/* Called when a filter instance is created and attach to a stream. It creates
3377 * the context that will be used to process this stream. */
3378static int
3379spoe_start(struct stream *s, struct filter *filter)
3380{
Christopher Faulet72bcc472017-01-04 16:39:41 +01003381 struct spoe_config *conf = FLT_CONF(filter);
3382 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003383 struct spoe_context *ctx;
3384
3385 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01003386 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003387 __FUNCTION__, s);
3388
Christopher Faulet8ef75252017-02-20 22:56:03 +01003389 ctx = spoe_create_context(filter);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003390 if (ctx == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01003391 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3392 " - failed to create SPOE context\n",
3393 (int)now.tv_sec, (int)now.tv_usec, agent->id,
3394 __FUNCTION__, ctx->strm);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003395 send_log(s->be, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01003396 "SPOE: [%s] failed to create SPOE context\n",
3397 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003398 return 0;
3399 }
3400
3401 ctx->strm = s;
3402 ctx->state = SPOE_CTX_ST_READY;
3403 filter->ctx = ctx;
3404
3405 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_FE]))
3406 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
3407
3408 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_BE]))
3409 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
3410
3411 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_RSP]))
3412 filter->pre_analyzers |= AN_RES_INSPECT;
3413
3414 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_FE]))
3415 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
3416
3417 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_BE]))
3418 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
3419
3420 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_RSP]))
3421 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
3422
3423 return 1;
3424}
3425
3426/* Called when a filter instance is detached from a stream. It release the
3427 * attached SPOE context. */
3428static void
3429spoe_stop(struct stream *s, struct filter *filter)
3430{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003431 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
3432 (int)now.tv_sec, (int)now.tv_usec,
3433 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3434 __FUNCTION__, s);
Christopher Faulet8ef75252017-02-20 22:56:03 +01003435 spoe_destroy_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003436}
3437
Christopher Fauletf7a30922016-11-10 15:04:51 +01003438
3439/*
3440 * Called when the stream is woken up because of expired timer.
3441 */
3442static void
3443spoe_check_timeouts(struct stream *s, struct filter *filter)
3444{
3445 struct spoe_context *ctx = filter->ctx;
3446
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003447 if (tick_is_expired(ctx->process_exp, now_ms)) {
3448 s->pending_events |= TASK_WOKEN_MSG;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003449 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003450 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003451}
3452
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003453/* Called when we are ready to filter data on a channel */
3454static int
3455spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3456{
3457 struct spoe_context *ctx = filter->ctx;
3458 int ret = 1;
3459
3460 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3461 " - ctx-flags=0x%08x\n",
3462 (int)now.tv_sec, (int)now.tv_usec,
3463 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3464 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3465
Christopher Fauletb067b062017-01-04 16:39:11 +01003466 if (ctx->state == SPOE_CTX_ST_NONE)
3467 goto out;
3468
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003469 if (!(chn->flags & CF_ISRESP)) {
3470 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
3471 chn->analysers |= AN_REQ_INSPECT_FE;
3472 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
3473 chn->analysers |= AN_REQ_INSPECT_BE;
3474
3475 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
3476 goto out;
3477
3478 ctx->stream_id = s->uniq_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003479 ret = spoe_process_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
Christopher Fauletb067b062017-01-04 16:39:11 +01003480 if (!ret)
3481 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003482 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
3483 }
3484 else {
3485 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
3486 chn->analysers |= AN_RES_INSPECT;
3487
3488 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
3489 goto out;
3490
Christopher Faulet8ef75252017-02-20 22:56:03 +01003491 ret = spoe_process_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003492 if (!ret) {
3493 channel_dont_read(chn);
3494 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01003495 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003496 }
Christopher Fauletb067b062017-01-04 16:39:11 +01003497 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003498 }
3499
3500 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003501 return ret;
3502}
3503
3504/* Called before a processing happens on a given channel */
3505static int
3506spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
3507 struct channel *chn, unsigned an_bit)
3508{
3509 struct spoe_context *ctx = filter->ctx;
3510 int ret = 1;
3511
3512 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3513 " - ctx-flags=0x%08x - ana=0x%08x\n",
3514 (int)now.tv_sec, (int)now.tv_usec,
3515 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3516 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
3517 ctx->flags, an_bit);
3518
Christopher Fauletb067b062017-01-04 16:39:11 +01003519 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003520 goto out;
3521
3522 switch (an_bit) {
3523 case AN_REQ_INSPECT_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003524 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003525 break;
3526 case AN_REQ_INSPECT_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003527 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003528 break;
3529 case AN_RES_INSPECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003530 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003531 break;
3532 case AN_REQ_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003533 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003534 break;
3535 case AN_REQ_HTTP_PROCESS_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003536 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003537 break;
3538 case AN_RES_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003539 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003540 break;
3541 }
3542
3543 out:
3544 if (!ret) {
3545 channel_dont_read(chn);
3546 channel_dont_close(chn);
3547 }
3548 return ret;
3549}
3550
3551/* Called when the filtering on the channel ends. */
3552static int
3553spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3554{
3555 struct spoe_context *ctx = filter->ctx;
3556
3557 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3558 " - ctx-flags=0x%08x\n",
3559 (int)now.tv_sec, (int)now.tv_usec,
3560 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3561 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3562
3563 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003564 spoe_reset_context(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003565 }
3566
3567 return 1;
3568}
3569
3570/********************************************************************
3571 * Functions that manage the filter initialization
3572 ********************************************************************/
3573struct flt_ops spoe_ops = {
3574 /* Manage SPOE filter, called for each filter declaration */
3575 .init = spoe_init,
3576 .deinit = spoe_deinit,
3577 .check = spoe_check,
3578
3579 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01003580 .attach = spoe_start,
3581 .detach = spoe_stop,
3582 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003583
3584 /* Handle channels activity */
3585 .channel_start_analyze = spoe_start_analyze,
3586 .channel_pre_analyze = spoe_chn_pre_analyze,
3587 .channel_end_analyze = spoe_end_analyze,
3588};
3589
3590
3591static int
3592cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
3593{
3594 const char *err;
3595 int i, err_code = 0;
3596
3597 if ((cfg_scope == NULL && curengine != NULL) ||
3598 (cfg_scope != NULL && curengine == NULL) ||
3599 strcmp(curengine, cfg_scope))
3600 goto out;
3601
3602 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
3603 if (!*args[1]) {
3604 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
3605 file, linenum);
3606 err_code |= ERR_ALERT | ERR_ABORT;
3607 goto out;
3608 }
3609 if (*args[2]) {
3610 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3611 file, linenum, args[2]);
3612 err_code |= ERR_ALERT | ERR_ABORT;
3613 goto out;
3614 }
3615
3616 err = invalid_char(args[1]);
3617 if (err) {
3618 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3619 file, linenum, *err, args[0], args[1]);
3620 err_code |= ERR_ALERT | ERR_ABORT;
3621 goto out;
3622 }
3623
3624 if (curagent != NULL) {
3625 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
3626 file, linenum);
3627 err_code |= ERR_ALERT | ERR_ABORT;
3628 goto out;
3629 }
3630 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
3631 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3632 err_code |= ERR_ALERT | ERR_ABORT;
3633 goto out;
3634 }
3635
3636 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003637
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003638 curagent->conf.file = strdup(file);
3639 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003640
3641 curagent->timeout.hello = TICK_ETERNITY;
3642 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003643 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003644
3645 curagent->engine_id = NULL;
3646 curagent->var_pfx = NULL;
3647 curagent->var_on_error = NULL;
3648 curagent->flags = 0;
3649 curagent->cps_max = 0;
3650 curagent->eps_max = 0;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003651 curagent->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003652 curagent->min_applets = 0;
3653 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003654
3655 for (i = 0; i < SPOE_EV_EVENTS; ++i)
3656 LIST_INIT(&curagent->messages[i]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003657
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003658 curagent->frame_size = curagent->max_frame_size;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003659 curagent->applets_act = 0;
3660 curagent->applets_idle = 0;
3661 curagent->sending_rate = 0;
3662
3663 LIST_INIT(&curagent->applets);
3664 LIST_INIT(&curagent->sending_queue);
3665 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003666 }
3667 else if (!strcmp(args[0], "use-backend")) {
3668 if (!*args[1]) {
3669 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3670 file, linenum, args[0]);
3671 err_code |= ERR_ALERT | ERR_FATAL;
3672 goto out;
3673 }
3674 if (*args[2]) {
3675 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3676 file, linenum, args[2]);
3677 err_code |= ERR_ALERT | ERR_ABORT;
3678 goto out;
3679 }
3680 free(curagent->b.name);
3681 curagent->b.name = strdup(args[1]);
3682 }
3683 else if (!strcmp(args[0], "messages")) {
3684 int cur_arg = 1;
3685 while (*args[cur_arg]) {
3686 struct spoe_msg_placeholder *mp = NULL;
3687
3688 list_for_each_entry(mp, &curmps, list) {
3689 if (!strcmp(mp->id, args[cur_arg])) {
3690 Alert("parsing [%s:%d]: spoe-message message '%s' already declared.\n",
3691 file, linenum, args[cur_arg]);
3692 err_code |= ERR_ALERT | ERR_FATAL;
3693 goto out;
3694 }
3695 }
3696
3697 if ((mp = calloc(1, sizeof(*mp))) == NULL) {
3698 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3699 err_code |= ERR_ALERT | ERR_ABORT;
3700 goto out;
3701 }
3702 mp->id = strdup(args[cur_arg]);
3703 LIST_ADDQ(&curmps, &mp->list);
3704 cur_arg++;
3705 }
3706 }
3707 else if (!strcmp(args[0], "timeout")) {
3708 unsigned int *tv = NULL;
3709 const char *res;
3710 unsigned timeout;
3711
3712 if (!*args[1]) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003713 Alert("parsing [%s:%d] : 'timeout' expects 'hello', 'idle' and 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003714 file, linenum);
3715 err_code |= ERR_ALERT | ERR_FATAL;
3716 goto out;
3717 }
3718 if (!strcmp(args[1], "hello"))
3719 tv = &curagent->timeout.hello;
3720 else if (!strcmp(args[1], "idle"))
3721 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003722 else if (!strcmp(args[1], "processing"))
3723 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003724 else {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003725 Alert("parsing [%s:%d] : 'timeout' supports 'hello', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003726 file, linenum, args[1]);
3727 err_code |= ERR_ALERT | ERR_FATAL;
3728 goto out;
3729 }
3730 if (!*args[2]) {
3731 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3732 file, linenum, args[1]);
3733 err_code |= ERR_ALERT | ERR_FATAL;
3734 goto out;
3735 }
3736 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3737 if (res) {
3738 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3739 file, linenum, *res, args[1]);
3740 err_code |= ERR_ALERT | ERR_ABORT;
3741 goto out;
3742 }
3743 if (*args[3]) {
3744 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3745 file, linenum, args[3]);
3746 err_code |= ERR_ALERT | ERR_ABORT;
3747 goto out;
3748 }
3749 *tv = MS_TO_TICKS(timeout);
3750 }
3751 else if (!strcmp(args[0], "option")) {
3752 if (!*args[1]) {
3753 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3754 file, linenum, args[0]);
3755 err_code |= ERR_ALERT | ERR_FATAL;
3756 goto out;
3757 }
3758 if (!strcmp(args[1], "var-prefix")) {
3759 char *tmp;
3760
3761 if (!*args[2]) {
3762 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3763 file, linenum, args[0],
3764 args[1]);
3765 err_code |= ERR_ALERT | ERR_FATAL;
3766 goto out;
3767 }
3768 tmp = args[2];
3769 while (*tmp) {
3770 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3771 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3772 file, linenum, args[0], args[1]);
3773 err_code |= ERR_ALERT | ERR_FATAL;
3774 goto out;
3775 }
3776 tmp++;
3777 }
3778 curagent->var_pfx = strdup(args[2]);
3779 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003780 else if (!strcmp(args[1], "continue-on-error")) {
3781 if (*args[2]) {
3782 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
Christopher Faulet48026722016-11-16 15:01:12 +01003783 file, linenum, args[2]);
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003784 err_code |= ERR_ALERT | ERR_ABORT;
3785 goto out;
3786 }
3787 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3788 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003789 else if (!strcmp(args[1], "set-on-error")) {
3790 char *tmp;
3791
3792 if (!*args[2]) {
3793 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3794 file, linenum, args[0],
3795 args[1]);
3796 err_code |= ERR_ALERT | ERR_FATAL;
3797 goto out;
3798 }
3799 tmp = args[2];
3800 while (*tmp) {
3801 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3802 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3803 file, linenum, args[0], args[1]);
3804 err_code |= ERR_ALERT | ERR_FATAL;
3805 goto out;
3806 }
3807 tmp++;
3808 }
3809 curagent->var_on_error = strdup(args[2]);
3810 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003811 else {
3812 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3813 file, linenum, args[1]);
3814 err_code |= ERR_ALERT | ERR_FATAL;
3815 goto out;
3816 }
Christopher Faulet48026722016-11-16 15:01:12 +01003817 }
3818 else if (!strcmp(args[0], "maxconnrate")) {
3819 if (!*args[1]) {
3820 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3821 file, linenum, args[0]);
3822 err_code |= ERR_ALERT | ERR_FATAL;
3823 goto out;
3824 }
3825 if (*args[2]) {
3826 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3827 file, linenum, args[2]);
3828 err_code |= ERR_ALERT | ERR_ABORT;
3829 goto out;
3830 }
3831 curagent->cps_max = atol(args[1]);
3832 }
3833 else if (!strcmp(args[0], "maxerrrate")) {
3834 if (!*args[1]) {
3835 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3836 file, linenum, args[0]);
3837 err_code |= ERR_ALERT | ERR_FATAL;
3838 goto out;
3839 }
3840 if (*args[2]) {
3841 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3842 file, linenum, args[2]);
3843 err_code |= ERR_ALERT | ERR_ABORT;
3844 goto out;
3845 }
3846 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003847 }
3848 else if (*args[0]) {
3849 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3850 file, linenum, args[0]);
3851 err_code |= ERR_ALERT | ERR_FATAL;
3852 goto out;
3853 }
3854 out:
3855 return err_code;
3856}
3857
3858static int
3859cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3860{
3861 struct spoe_message *msg;
3862 struct spoe_arg *arg;
3863 const char *err;
3864 char *errmsg = NULL;
3865 int err_code = 0;
3866
3867 if ((cfg_scope == NULL && curengine != NULL) ||
3868 (cfg_scope != NULL && curengine == NULL) ||
3869 strcmp(curengine, cfg_scope))
3870 goto out;
3871
3872 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3873 if (!*args[1]) {
3874 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3875 file, linenum);
3876 err_code |= ERR_ALERT | ERR_ABORT;
3877 goto out;
3878 }
3879 if (*args[2]) {
3880 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3881 file, linenum, args[2]);
3882 err_code |= ERR_ALERT | ERR_ABORT;
3883 goto out;
3884 }
3885
3886 err = invalid_char(args[1]);
3887 if (err) {
3888 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3889 file, linenum, *err, args[0], args[1]);
3890 err_code |= ERR_ALERT | ERR_ABORT;
3891 goto out;
3892 }
3893
3894 list_for_each_entry(msg, &curmsgs, list) {
3895 if (!strcmp(msg->id, args[1])) {
3896 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3897 " name as another one declared at %s:%d.\n",
3898 file, linenum, args[1], msg->conf.file, msg->conf.line);
3899 err_code |= ERR_ALERT | ERR_FATAL;
3900 goto out;
3901 }
3902 }
3903
3904 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3905 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3906 err_code |= ERR_ALERT | ERR_ABORT;
3907 goto out;
3908 }
3909
3910 curmsg->id = strdup(args[1]);
3911 curmsg->id_len = strlen(curmsg->id);
3912 curmsg->event = SPOE_EV_NONE;
3913 curmsg->conf.file = strdup(file);
3914 curmsg->conf.line = linenum;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003915 curmsg->nargs = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003916 LIST_INIT(&curmsg->args);
3917 LIST_ADDQ(&curmsgs, &curmsg->list);
3918 }
3919 else if (!strcmp(args[0], "args")) {
3920 int cur_arg = 1;
3921
3922 curproxy->conf.args.ctx = ARGC_SPOE;
3923 curproxy->conf.args.file = file;
3924 curproxy->conf.args.line = linenum;
3925 while (*args[cur_arg]) {
3926 char *delim = strchr(args[cur_arg], '=');
3927 int idx = 0;
3928
3929 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3930 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3931 err_code |= ERR_ALERT | ERR_ABORT;
3932 goto out;
3933 }
3934
3935 if (!delim) {
3936 arg->name = NULL;
3937 arg->name_len = 0;
3938 delim = args[cur_arg];
3939 }
3940 else {
3941 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3942 arg->name_len = delim - args[cur_arg];
3943 delim++;
3944 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003945 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3946 &idx, file, linenum, &errmsg,
3947 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003948 if (arg->expr == NULL) {
3949 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3950 err_code |= ERR_ALERT | ERR_FATAL;
3951 free(arg->name);
3952 free(arg);
3953 goto out;
3954 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003955 curmsg->nargs++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003956 LIST_ADDQ(&curmsg->args, &arg->list);
3957 cur_arg++;
3958 }
3959 curproxy->conf.args.file = NULL;
3960 curproxy->conf.args.line = 0;
3961 }
3962 else if (!strcmp(args[0], "event")) {
3963 if (!*args[1]) {
3964 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
3965 err_code |= ERR_ALERT | ERR_ABORT;
3966 goto out;
3967 }
3968 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3969 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3970 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3971 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3972
3973 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3974 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3975 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3976 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3977 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3978 curmsg->event = SPOE_EV_ON_TCP_RSP;
3979
3980 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3981 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3982 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3983 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3984 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3985 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3986 else {
3987 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3988 file, linenum, args[1]);
3989 err_code |= ERR_ALERT | ERR_ABORT;
3990 goto out;
3991 }
3992 }
3993 else if (!*args[0]) {
3994 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3995 file, linenum, args[0]);
3996 err_code |= ERR_ALERT | ERR_FATAL;
3997 goto out;
3998 }
3999 out:
4000 free(errmsg);
4001 return err_code;
4002}
4003
4004/* Return -1 on error, else 0 */
4005static int
4006parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
4007 struct flt_conf *fconf, char **err, void *private)
4008{
4009 struct list backup_sections;
4010 struct spoe_config *conf;
4011 struct spoe_message *msg, *msgback;
4012 struct spoe_msg_placeholder *mp, *mpback;
4013 char *file = NULL, *engine = NULL;
4014 int ret, pos = *cur_arg + 1;
4015
4016 conf = calloc(1, sizeof(*conf));
4017 if (conf == NULL) {
4018 memprintf(err, "%s: out of memory", args[*cur_arg]);
4019 goto error;
4020 }
4021 conf->proxy = px;
4022
4023 while (*args[pos]) {
4024 if (!strcmp(args[pos], "config")) {
4025 if (!*args[pos+1]) {
4026 memprintf(err, "'%s' : '%s' option without value",
4027 args[*cur_arg], args[pos]);
4028 goto error;
4029 }
4030 file = args[pos+1];
4031 pos += 2;
4032 }
4033 else if (!strcmp(args[pos], "engine")) {
4034 if (!*args[pos+1]) {
4035 memprintf(err, "'%s' : '%s' option without value",
4036 args[*cur_arg], args[pos]);
4037 goto error;
4038 }
4039 engine = args[pos+1];
4040 pos += 2;
4041 }
4042 else {
4043 memprintf(err, "unknown keyword '%s'", args[pos]);
4044 goto error;
4045 }
4046 }
4047 if (file == NULL) {
4048 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
4049 goto error;
4050 }
4051
4052 /* backup sections and register SPOE sections */
4053 LIST_INIT(&backup_sections);
4054 cfg_backup_sections(&backup_sections);
4055 cfg_register_section("spoe-agent", cfg_parse_spoe_agent);
4056 cfg_register_section("spoe-message", cfg_parse_spoe_message);
4057
4058 /* Parse SPOE filter configuration file */
4059 curengine = engine;
4060 curproxy = px;
4061 curagent = NULL;
4062 curmsg = NULL;
4063 ret = readcfgfile(file);
4064 curproxy = NULL;
4065
4066 /* unregister SPOE sections and restore previous sections */
4067 cfg_unregister_sections();
4068 cfg_restore_sections(&backup_sections);
4069
4070 if (ret == -1) {
4071 memprintf(err, "Could not open configuration file %s : %s",
4072 file, strerror(errno));
4073 goto error;
4074 }
4075 if (ret & (ERR_ABORT|ERR_FATAL)) {
4076 memprintf(err, "Error(s) found in configuration file %s", file);
4077 goto error;
4078 }
4079
4080 /* Check SPOE agent */
4081 if (curagent == NULL) {
4082 memprintf(err, "No SPOE agent found in file %s", file);
4083 goto error;
4084 }
4085 if (curagent->b.name == NULL) {
4086 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
4087 curagent->id, curagent->conf.file, curagent->conf.line);
4088 goto error;
4089 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01004090 if (curagent->timeout.hello == TICK_ETERNITY ||
4091 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01004092 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004093 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
4094 " | While not properly invalid, you will certainly encounter various problems\n"
4095 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01004096 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004097 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
4098 }
4099 if (curagent->var_pfx == NULL) {
4100 char *tmp = curagent->id;
4101
4102 while (*tmp) {
4103 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
4104 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
4105 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
4106 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
4107 goto error;
4108 }
4109 tmp++;
4110 }
4111 curagent->var_pfx = strdup(curagent->id);
4112 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01004113 if (curagent->engine_id == NULL)
4114 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004115
4116 if (LIST_ISEMPTY(&curmps)) {
4117 Warning("Proxy '%s': No message used by SPOE agent '%s' declared at %s:%d.\n",
4118 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
4119 goto finish;
4120 }
4121
4122 list_for_each_entry_safe(mp, mpback, &curmps, list) {
4123 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01004124 struct spoe_arg *arg;
4125 unsigned int where;
4126
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004127 if (!strcmp(msg->id, mp->id)) {
4128 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
4129 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
4130 msg->event = SPOE_EV_ON_TCP_REQ_FE;
4131 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
4132 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
4133 }
4134 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
4135 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
4136 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
4137 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
4138 px->id, msg->conf.file, msg->conf.line);
4139 goto next;
4140 }
4141 if (msg->event == SPOE_EV_NONE) {
4142 Warning("Proxy '%s': Ignore SPOE message without event at %s:%d.\n",
4143 px->id, msg->conf.file, msg->conf.line);
4144 goto next;
4145 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01004146
4147 where = 0;
4148 switch (msg->event) {
4149 case SPOE_EV_ON_CLIENT_SESS:
4150 where |= SMP_VAL_FE_CON_ACC;
4151 break;
4152
4153 case SPOE_EV_ON_TCP_REQ_FE:
4154 where |= SMP_VAL_FE_REQ_CNT;
4155 break;
4156
4157 case SPOE_EV_ON_HTTP_REQ_FE:
4158 where |= SMP_VAL_FE_HRQ_HDR;
4159 break;
4160
4161 case SPOE_EV_ON_TCP_REQ_BE:
4162 if (px->cap & PR_CAP_FE)
4163 where |= SMP_VAL_FE_REQ_CNT;
4164 if (px->cap & PR_CAP_BE)
4165 where |= SMP_VAL_BE_REQ_CNT;
4166 break;
4167
4168 case SPOE_EV_ON_HTTP_REQ_BE:
4169 if (px->cap & PR_CAP_FE)
4170 where |= SMP_VAL_FE_HRQ_HDR;
4171 if (px->cap & PR_CAP_BE)
4172 where |= SMP_VAL_BE_HRQ_HDR;
4173 break;
4174
4175 case SPOE_EV_ON_SERVER_SESS:
4176 where |= SMP_VAL_BE_SRV_CON;
4177 break;
4178
4179 case SPOE_EV_ON_TCP_RSP:
4180 if (px->cap & PR_CAP_FE)
4181 where |= SMP_VAL_FE_RES_CNT;
4182 if (px->cap & PR_CAP_BE)
4183 where |= SMP_VAL_BE_RES_CNT;
4184 break;
4185
4186 case SPOE_EV_ON_HTTP_RSP:
4187 if (px->cap & PR_CAP_FE)
4188 where |= SMP_VAL_FE_HRS_HDR;
4189 if (px->cap & PR_CAP_BE)
4190 where |= SMP_VAL_BE_HRS_HDR;
4191 break;
4192
4193 default:
4194 break;
4195 }
4196
4197 list_for_each_entry(arg, &msg->args, list) {
4198 if (!(arg->expr->fetch->val & where)) {
4199 Warning("Proxy '%s': Ignore SPOE message at %s:%d: "
4200 "some args extract information from '%s', "
4201 "none of which is available here ('%s').\n",
4202 px->id, msg->conf.file, msg->conf.line,
4203 sample_ckp_names(arg->expr->fetch->use),
4204 sample_ckp_names(where));
4205 goto next;
4206 }
4207 }
4208
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004209 msg->agent = curagent;
4210 LIST_DEL(&msg->list);
4211 LIST_ADDQ(&curagent->messages[msg->event], &msg->list);
4212 goto next;
4213 }
4214 }
4215 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
4216 curagent->id, mp->id, curagent->conf.file, curagent->conf.line);
4217 goto error;
4218 next:
4219 continue;
4220 }
4221
4222 finish:
4223 conf->agent = curagent;
4224 list_for_each_entry_safe(mp, mpback, &curmps, list) {
4225 LIST_DEL(&mp->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004226 spoe_release_msg_placeholder(mp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004227 }
4228 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4229 Warning("Proxy '%s': Ignore unused SPOE messages '%s' declared at %s:%d.\n",
4230 px->id, msg->id, msg->conf.file, msg->conf.line);
4231 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004232 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004233 }
4234
4235 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01004236 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004237 fconf->ops = &spoe_ops;
4238 fconf->conf = conf;
4239 return 0;
4240
4241 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01004242 spoe_release_agent(curagent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004243 list_for_each_entry_safe(mp, mpback, &curmps, list) {
4244 LIST_DEL(&mp->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004245 spoe_release_msg_placeholder(mp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004246 }
4247 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4248 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004249 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004250 }
4251 free(conf);
4252 return -1;
4253}
4254
4255
4256/* Declare the filter parser for "spoe" keyword */
4257static struct flt_kw_list flt_kws = { "SPOE", { }, {
4258 { "spoe", parse_spoe_flt, NULL },
4259 { NULL, NULL, NULL },
4260 }
4261};
4262
4263__attribute__((constructor))
4264static void __spoe_init(void)
4265{
4266 flt_register_keywords(&flt_kws);
4267
4268 LIST_INIT(&curmsgs);
4269 LIST_INIT(&curmps);
4270 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004271 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004272}
4273
4274__attribute__((destructor))
4275static void
4276__spoe_deinit(void)
4277{
4278 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004279 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004280}