Coverage Report

Created: 2026-04-03 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/flt_spoe.c
Line
Count
Source
1
/*
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 <haproxy/acl.h>
16
#include <haproxy/applet.h>
17
#include <haproxy/action-t.h>
18
#include <haproxy/api.h>
19
#include <haproxy/arg.h>
20
#include <haproxy/cfgparse.h>
21
#include <haproxy/check.h>
22
#include <haproxy/filters.h>
23
#include <haproxy/freq_ctr.h>
24
#include <haproxy/frontend.h>
25
#include <haproxy/global.h>
26
#include <haproxy/http_rules.h>
27
#include <haproxy/log.h>
28
#include <haproxy/pool.h>
29
#include <haproxy/proxy.h>
30
#include <haproxy/sample.h>
31
#include <haproxy/sc_strm.h>
32
#include <haproxy/session.h>
33
#include <haproxy/signal.h>
34
#include <haproxy/sink.h>
35
#include <haproxy/spoe.h>
36
#include <haproxy/stconn.h>
37
#include <haproxy/stream.h>
38
#include <haproxy/task.h>
39
#include <haproxy/tcp_rules.h>
40
#include <haproxy/thread.h>
41
#include <haproxy/time.h>
42
#include <haproxy/tools.h>
43
#include <haproxy/vars.h>
44
45
46
/* Type of list of messages */
47
0
#define SPOE_MSGS_BY_EVENT 0x01
48
0
#define SPOE_MSGS_BY_GROUP 0x02
49
50
/* Flags set on the SPOE context */
51
0
#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
52
0
#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
53
0
#define SPOE_CTX_FL_REQ_PROCESS   0x00000004 /* Set when SPOE is processing the request */
54
0
#define SPOE_CTX_FL_RSP_PROCESS   0x00000008 /* Set when SPOE is processing the response */
55
/* unused 0x00000010 */
56
57
0
#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
58
59
/* All possible states for a SPOE context */
60
enum spoe_ctx_state {
61
  SPOE_CTX_ST_NONE = 0,
62
  SPOE_CTX_ST_READY,
63
  SPOE_CTX_ST_ENCODING_MSGS,
64
  SPOE_CTX_ST_SENDING_MSGS,
65
  SPOE_CTX_ST_WAITING_ACK,
66
  SPOE_CTX_ST_DONE,
67
  SPOE_CTX_ST_ERROR,
68
};
69
70
/* All possible states for a SPOE applet */
71
enum spoe_appctx_state {
72
  SPOE_APPCTX_ST_WAITING_ACK = 0,
73
  SPOE_APPCTX_ST_EXIT,
74
  SPOE_APPCTX_ST_END,
75
};
76
77
/* All supported SPOE events */
78
enum spoe_event {
79
  SPOE_EV_NONE = 0,
80
81
  /* Request events */
82
  SPOE_EV_ON_CLIENT_SESS = 1,
83
  SPOE_EV_ON_TCP_REQ_FE,
84
  SPOE_EV_ON_TCP_REQ_BE,
85
  SPOE_EV_ON_HTTP_REQ_FE,
86
  SPOE_EV_ON_HTTP_REQ_BE,
87
88
  /* Response events */
89
  SPOE_EV_ON_SERVER_SESS,
90
  SPOE_EV_ON_TCP_RSP,
91
  SPOE_EV_ON_HTTP_RSP,
92
93
  SPOE_EV_EVENTS
94
};
95
96
/* Errors triggered by streams */
97
enum spoe_context_error {
98
  SPOE_CTX_ERR_NONE = 0,
99
  SPOE_CTX_ERR_TOUT,
100
  SPOE_CTX_ERR_RES,
101
  SPOE_CTX_ERR_TOO_BIG,
102
  SPOE_CTX_ERR_INTERRUPT,
103
  SPOE_CTX_ERR_UNKNOWN = 255,
104
  SPOE_CTX_ERRS,
105
};
106
107
/* Describe an argument that will be linked to a message. It is a sample fetch,
108
 * with an optional name. */
109
struct spoe_arg {
110
  char               *name;     /* Name of the argument, may be NULL */
111
  unsigned int        name_len; /* The name length, 0 if NULL */
112
  struct sample_expr *expr;     /* Sample expression */
113
  struct list         list;     /* Used to chain SPOE args */
114
};
115
116
/* Used during the config parsing only because, when a SPOE agent section is
117
 * parsed, messages/groups can be undefined. */
118
struct spoe_placeholder {
119
  char       *id;    /* SPOE placeholder id */
120
  struct list list;  /* Use to chain SPOE placeholders */
121
};
122
123
/* Used during the config parsing, when SPOE agent section is parsed, to
124
 * register some variable names. */
125
struct spoe_var_placeholder {
126
  char        *name;  /* The variable name */
127
  struct list  list;  /* Use to chain SPOE var placeholders */
128
};
129
130
/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
131
 * an argument list (see above) and it is linked to a specific event. */
132
struct spoe_message {
133
  char               *id;     /* SPOE message id */
134
  unsigned int        id_len; /* The message id length */
135
  struct spoe_agent  *agent;  /* SPOE agent owning this SPOE message */
136
  struct spoe_group  *group;  /* SPOE group owning this SPOE message (can be NULL) */
137
        struct {
138
                char       *file;   /* file where the SPOE message appears */
139
                int         line;   /* line where the SPOE message appears */
140
        } conf;                     /* config information */
141
  unsigned int        nargs;  /* # of arguments */
142
  struct list         args;   /* Arguments added when the SPOE messages is sent */
143
  struct list         list;   /* Used to chain SPOE messages */
144
  struct list         by_evt; /* By event list */
145
  struct list         by_grp; /* By group list */
146
147
  struct list         acls;   /* ACL declared on this message */
148
  struct acl_cond    *cond;   /* acl condition to meet */
149
  enum spoe_event     event;  /* SPOE_EV_* */
150
};
151
152
/* Describe a group of messages that will be sent in a NOTIFY frame. A group has
153
 * a name and a list of messages. It can be used by HAProxy, outside events
154
 * processing, mainly in (tcp|http) rules. */
155
struct spoe_group {
156
  char              *id;      /* SPOE group id */
157
  struct spoe_agent *agent;   /* SPOE agent owning this SPOE group */
158
        struct {
159
                char      *file;    /* file where the SPOE group appears */
160
                int        line;    /* line where the SPOE group appears */
161
        } conf;                     /* config information */
162
163
  struct list phs;      /* List of placeholders used during conf parsing */
164
  struct list messages; /* List of SPOE messages that will be sent by this
165
             * group */
166
167
  struct list list;     /* Used to chain SPOE groups */
168
};
169
170
171
/* SPOE context attached to a stream. It is the main structure that handles the
172
 * processing offload */
173
struct spoe_context {
174
  struct filter      *filter;       /* The SPOE filter */
175
  struct stream      *strm;         /* The stream that should be offloaded */
176
177
  struct list        *events;       /* List of messages that will be sent during the stream processing */
178
  struct list        *groups;       /* List of available SPOE group */
179
180
  struct buffer       buffer;       /* Buffer used to store a encoded messages */
181
  struct buffer_wait  buffer_wait;  /* position in the list of resources waiting for a buffer */
182
183
  enum spoe_ctx_state state;        /* SPOE_CTX_ST_* */
184
  unsigned int        flags;        /* SPOE_CTX_FL_* */
185
  unsigned int        status_code;  /* SPOE_CTX_ERR_* */
186
187
  unsigned int        stream_id;    /* stream_id and frame_id are used */
188
  unsigned int        frame_id;     /* to map NOTIFY and ACK frames */
189
  unsigned int        process_exp;  /* expiration date to process an event */
190
191
  struct spoe_appctx *spoe_appctx; /* SPOE appctx sending the current frame */
192
193
  struct {
194
    ullong         start_ts;    /* start date of the current event/group */
195
    long           t_process;   /* processing time of the last event/group */
196
    unsigned long  t_total;     /* cumulative processing time */
197
  } stats; /* Stats for this stream */
198
};
199
200
/* SPOE context inside a appctx */
201
struct spoe_appctx {
202
  struct appctx      *owner;          /* the owner */
203
  struct spoe_agent  *agent;          /* agent on which the applet is attached */
204
  unsigned int        flags;          /* SPOE_APPCTX_FL_* */
205
  unsigned int        status_code;    /* SPOE_FRM_ERR_* */
206
  struct spoe_context *spoe_ctx;      /* The SPOE context to handle */
207
};
208
209
/* SPOE filter configuration */
210
struct spoe_config {
211
  char              *id;          /* The SPOE engine name. If undefined in HAProxy config,
212
           * it will be set with the SPOE agent name */
213
  struct proxy      *proxy;       /* Proxy owning the filter */
214
  struct spoe_agent *agent;       /* Agent used by this filter */
215
};
216
217
218
/* Helper to get SPOE ctx inside an appctx */
219
0
#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->svcctx))
220
221
/* SPOE filter id. Used to identify SPOE filters */
222
const char *spoe_filter_id = "SPOE filter";
223
224
/* The name of the SPOE engine, used during the parsing */
225
char *curengine = NULL;
226
227
/* SPOE agent used during the parsing */
228
/* SPOE agent/group/message used during the parsing */
229
struct spoe_agent   *curagent = NULL;
230
struct spoe_group   *curgrp   = NULL;
231
struct spoe_message *curmsg   = NULL;
232
233
/* list of SPOE messages and placeholders used during the parsing */
234
struct list curmsgs;
235
struct list curgrps;
236
struct list curmphs;
237
struct list curgphs;
238
struct list curvars;
239
240
/* list of log servers used during the parsing */
241
struct list curloggers;
242
243
/* agent's proxy flags (PR_O_* and PR_O2_*) used during parsing */
244
int curpxopts;
245
int curpxopts2;
246
247
/* Pools used to allocate SPOE structs */
248
DECLARE_STATIC_TYPED_POOL(pool_head_spoe_ctx,    "spoe_ctx",    struct spoe_context);
249
DECLARE_STATIC_TYPED_POOL(pool_head_spoe_appctx, "spoe_appctx", struct spoe_appctx);
250
251
struct flt_ops spoe_ops;
252
253
static int  spoe_acquire_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
254
static void spoe_release_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
255
static struct appctx *spoe_create_appctx(struct spoe_context *ctx);
256
257
/********************************************************************
258
 * helper functions/globals
259
 ********************************************************************/
260
static void spoe_release_placeholder(struct spoe_placeholder *ph)
261
0
{
262
0
  if (!ph)
263
0
    return;
264
0
  free(ph->id);
265
0
  free(ph);
266
0
}
267
268
static void spoe_release_message(struct spoe_message *msg)
269
0
{
270
0
  struct spoe_arg *arg, *argback;
271
0
  struct acl      *acl, *aclback;
272
273
0
  if (!msg)
274
0
    return;
275
0
  free(msg->id);
276
0
  free(msg->conf.file);
277
0
  list_for_each_entry_safe(arg, argback, &msg->args, list) {
278
0
    release_sample_expr(arg->expr);
279
0
    free(arg->name);
280
0
    LIST_DELETE(&arg->list);
281
0
    free(arg);
282
0
  }
283
0
  list_for_each_entry_safe(acl, aclback, &msg->acls, list) {
284
0
    LIST_DELETE(&acl->list);
285
0
    prune_acl(acl);
286
0
    free(acl);
287
0
  }
288
0
  free_acl_cond(msg->cond);
289
0
  free(msg);
290
0
}
291
292
static void spoe_release_group(struct spoe_group *grp)
293
0
{
294
0
  if (!grp)
295
0
    return;
296
0
  free(grp->id);
297
0
  free(grp->conf.file);
298
0
  free(grp);
299
0
}
300
301
static void spoe_release_agent(struct spoe_agent *agent)
302
0
{
303
0
  struct spoe_message *msg, *msgback;
304
0
  struct spoe_group   *grp, *grpback;
305
306
0
  if (!agent)
307
0
    return;
308
0
  free(agent->id);
309
0
  free(agent->conf.file);
310
0
  free(agent->var_pfx);
311
0
  free(agent->var_on_error);
312
0
  free(agent->var_t_process);
313
0
  free(agent->var_t_total);
314
0
  list_for_each_entry_safe(msg, msgback, &agent->messages, list) {
315
0
    LIST_DELETE(&msg->list);
316
0
    spoe_release_message(msg);
317
0
  }
318
0
  list_for_each_entry_safe(grp, grpback, &agent->groups, list) {
319
0
    LIST_DELETE(&grp->list);
320
0
    spoe_release_group(grp);
321
0
  }
322
0
  free(agent->events);
323
0
  free(agent->engine_id);
324
0
  if (agent->fe.id)
325
0
    deinit_proxy(&agent->fe);
326
0
  free(agent);
327
0
}
328
329
static const char *spoe_event_str[SPOE_EV_EVENTS] = {
330
  [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
331
  [SPOE_EV_ON_TCP_REQ_FE]  = "on-frontend-tcp-request",
332
  [SPOE_EV_ON_TCP_REQ_BE]  = "on-backend-tcp-request",
333
  [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
334
  [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
335
336
  [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
337
  [SPOE_EV_ON_TCP_RSP]     = "on-tcp-response",
338
  [SPOE_EV_ON_HTTP_RSP]    = "on-http-response",
339
};
340
341
342
/* Used to generates a unique id for an engine. On success, it returns a
343
 * allocated string. So it is the caller's responsibility to release it. If the
344
 * allocation failed, it returns NULL. */
345
static char *generate_pseudo_uuid()
346
0
{
347
0
  ha_generate_uuid_v4(&trash);
348
0
  return my_strndup(trash.area, trash.data);
349
0
}
350
351
/* set/add to <t> the elapsed time since <since> and now */
352
static inline void spoe_update_stat_time(ullong *since, long *t)
353
0
{
354
0
  if (*t == -1)
355
0
    *t = ns_to_ms(now_ns - *since);
356
0
  else
357
0
    *t += ns_to_ms(now_ns - *since);
358
0
  *since = 0;
359
0
}
360
361
/********************************************************************
362
 * Functions that manage the SPOE applet
363
 ********************************************************************/
364
struct spoe_agent *spoe_appctx_agent(struct appctx *appctx)
365
0
{
366
0
  struct spoe_appctx  *spoe_appctx;
367
368
0
  if (!appctx)
369
0
    return NULL;
370
371
0
  spoe_appctx = SPOE_APPCTX(appctx);
372
0
  if (!spoe_appctx)
373
0
    return NULL;
374
375
0
  return spoe_appctx->agent;
376
0
}
377
378
static int spoe_init_appctx(struct appctx *appctx)
379
0
{
380
0
  struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
381
0
  struct spoe_agent *agent = spoe_appctx->agent;
382
0
  struct stream *s;
383
384
0
  if (appctx_finalize_startup(appctx, &agent->fe, &spoe_appctx->spoe_ctx->buffer) == -1)
385
0
    goto error;
386
387
0
  spoe_appctx->owner = appctx;
388
389
0
  s = appctx_strm(appctx);
390
0
  stream_set_backend(s, agent->b.be);
391
392
  /* applet is waiting for data */
393
0
  applet_need_more_data(appctx);
394
395
0
  s->do_log = NULL;
396
0
  s->scb->flags |= SC_FL_RCV_ONCE | SC_FL_NOHALF;
397
0
  s->scf->flags |= SC_FL_NOHALF;
398
0
  s->parent = spoe_appctx->spoe_ctx->strm;
399
400
  /* The frame was forwarded to the SPOP mux, set EOI now */
401
0
  applet_set_eoi(appctx);
402
403
0
  appctx->st0 = SPOE_APPCTX_ST_WAITING_ACK;
404
0
  appctx_wakeup(appctx);
405
0
  return 0;
406
407
0
  error:
408
0
  return -1;
409
0
}
410
411
static void spoe_shut_appctx(struct appctx *appctx, enum se_shut_mode mode, struct se_abort_info *reason)
412
0
{
413
0
  struct spoe_appctx  *spoe_appctx = SPOE_APPCTX(appctx);
414
415
0
  if (!reason || spoe_appctx->status_code != SPOP_ERR_NONE)
416
0
    return;
417
418
0
  if (((reason->info & SE_ABRT_SRC_MASK) >> SE_ABRT_SRC_SHIFT) == SE_ABRT_SRC_MUX_SPOP)
419
0
    spoe_appctx->status_code = reason->code;
420
0
}
421
422
/* Callback function that releases a SPOE applet. This happens when the
423
 * connection with the agent is closed. */
424
static void spoe_release_appctx(struct appctx *appctx)
425
0
{
426
0
  struct spoe_appctx  *spoe_appctx = SPOE_APPCTX(appctx);
427
428
0
  if (spoe_appctx == NULL)
429
0
    return;
430
431
0
  appctx->svcctx = NULL;
432
0
  appctx_strm(appctx)->parent = NULL;
433
434
  /* Shutdown the server connection, if needed */
435
0
  if (appctx->st0 != SPOE_APPCTX_ST_END) {
436
0
    appctx->st0 = SPOE_APPCTX_ST_END;
437
0
    applet_set_error(appctx);
438
0
    if (spoe_appctx->status_code == SPOP_ERR_NONE)
439
0
      spoe_appctx->status_code = SPOP_ERR_IO;
440
0
  }
441
442
0
  if (spoe_appctx->spoe_ctx)  {
443
    /* Report an error to stream */
444
0
    spoe_appctx->spoe_ctx->spoe_appctx = NULL;
445
0
    if (spoe_appctx->spoe_ctx->state != SPOE_CTX_ST_DONE) {
446
0
      spoe_appctx->spoe_ctx->state = SPOE_CTX_ST_ERROR;
447
0
      spoe_appctx->spoe_ctx->status_code = (spoe_appctx->status_code + 0x100);
448
0
    }
449
0
    task_wakeup(spoe_appctx->spoe_ctx->strm->task, TASK_WOKEN_MSG);
450
0
  }
451
452
0
  end:
453
  /* Release allocated memory */
454
0
  pool_free(pool_head_spoe_appctx, spoe_appctx);
455
0
}
456
457
static int spoe_handle_receiving_frame_appctx(struct appctx *appctx)
458
0
{
459
0
  struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
460
0
  struct spoe_context *spoe_ctx = spoe_appctx->spoe_ctx;
461
0
  int ret = 0;
462
463
0
  BUG_ON(b_data(&spoe_ctx->buffer));
464
465
0
  if (!b_data(&appctx->inbuf)) {
466
0
    applet_need_more_data(appctx);
467
0
    goto end;
468
0
  }
469
470
0
  if (!spoe_acquire_buffer(&spoe_ctx->buffer, &spoe_ctx->buffer_wait))
471
0
    goto end;
472
473
0
  if (b_data(&appctx->inbuf) > spoe_appctx->agent->max_frame_size) {
474
0
    spoe_ctx->state = SPOE_CTX_ST_ERROR;
475
0
    spoe_ctx->status_code = (spoe_appctx->status_code + 0x100);
476
0
    spoe_appctx->status_code = SPOP_ERR_TOO_BIG;
477
0
    appctx->st0 = SPOE_APPCTX_ST_EXIT;
478
0
    task_wakeup(spoe_ctx->strm->task, TASK_WOKEN_MSG);
479
0
    ret = -1;
480
0
    goto end;
481
0
  }
482
483
0
  b_xfer(&spoe_ctx->buffer, &appctx->inbuf, b_data(&appctx->inbuf));
484
0
  spoe_ctx->state = SPOE_CTX_ST_DONE;
485
0
  appctx->st0 = SPOE_APPCTX_ST_EXIT;
486
0
  task_wakeup(spoe_ctx->strm->task, TASK_WOKEN_MSG);
487
0
  ret = 1;
488
489
0
  end:
490
0
  return ret;
491
0
}
492
493
/* I/O Handler processing messages exchanged with the agent */
494
static void spoe_handle_appctx(struct appctx *appctx)
495
0
{
496
0
  if (SPOE_APPCTX(appctx) == NULL)
497
0
    goto out;
498
499
0
  if (applet_fl_test(appctx, APPCTX_FL_INBLK_ALLOC))
500
0
    goto out;
501
502
0
  if (!appctx_get_buf(appctx, &appctx->inbuf))
503
0
    goto out;
504
505
0
  if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
506
0
    b_reset(&appctx->inbuf);
507
0
    applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
508
0
    goto out;
509
0
  }
510
511
0
  switchstate:
512
0
  switch (appctx->st0) {
513
    /* case SPOE_APPCTX_ST_PROCESSING: */
514
0
    case SPOE_APPCTX_ST_WAITING_ACK:
515
0
      if (!SPOE_APPCTX(appctx)->spoe_ctx) {
516
0
        appctx->st0 = SPOE_APPCTX_ST_END;
517
0
        applet_set_error(appctx);
518
0
      }
519
0
      else {
520
0
        SPOE_APPCTX(appctx)->spoe_ctx->state = SPOE_CTX_ST_WAITING_ACK;
521
0
        if (!spoe_handle_receiving_frame_appctx(appctx))
522
0
          break;
523
0
      }
524
0
      goto switchstate;
525
526
0
    case SPOE_APPCTX_ST_EXIT:
527
0
      if (SPOE_APPCTX(appctx)->status_code != SPOP_ERR_NONE)
528
0
        applet_set_error(appctx);
529
0
      if (!SPOE_APPCTX(appctx)->spoe_ctx) {
530
0
        appctx->st0 = SPOE_APPCTX_ST_END;
531
0
        applet_set_eos(appctx);
532
0
        goto switchstate;
533
0
      }
534
0
      break;
535
536
0
    case SPOE_APPCTX_ST_END:
537
0
      b_reset(&appctx->inbuf);
538
0
      applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
539
0
      break;
540
0
  }
541
542
0
  out:
543
0
  applet_have_no_more_data(appctx);
544
0
  return;
545
0
}
546
547
struct applet spoe_applet = {
548
  .obj_type = OBJ_TYPE_APPLET,
549
  .flags = APPLET_FL_NEW_API,
550
  .name = "<SPOE>", /* used for logging */
551
  .fct = spoe_handle_appctx,
552
  .init = spoe_init_appctx,
553
  .shut = spoe_shut_appctx,
554
  .rcv_buf = appctx_raw_rcv_buf,
555
  .snd_buf = appctx_raw_snd_buf,
556
  .release = spoe_release_appctx,
557
};
558
559
/* Create a SPOE applet. On success, the created applet is returned, else
560
 * NULL. */
561
static struct appctx *spoe_create_appctx(struct spoe_context *ctx)
562
0
{
563
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
564
0
  struct spoe_agent *agent = conf->agent;
565
0
  struct spoe_appctx *spoe_appctx;
566
0
  struct appctx *appctx;
567
568
0
  spoe_appctx = pool_zalloc(pool_head_spoe_appctx);
569
0
  if (spoe_appctx == NULL)
570
0
    goto out_error;
571
572
0
  spoe_appctx->agent           = agent;
573
0
  spoe_appctx->flags           = 0;
574
0
  spoe_appctx->status_code     = SPOP_ERR_NONE;
575
0
  spoe_appctx->spoe_ctx        = ctx;
576
0
  ctx->spoe_appctx             = spoe_appctx;
577
578
0
  if ((appctx = appctx_new_here(&spoe_applet, NULL)) == NULL)
579
0
    goto out_free_spoe_appctx;
580
581
0
  appctx->svcctx = spoe_appctx;
582
0
  if (appctx_init(appctx) == -1)
583
0
    goto out_free_appctx;
584
585
0
  appctx_wakeup(appctx);
586
0
  return appctx;
587
588
  /* Error unrolling */
589
0
 out_free_appctx:
590
0
  appctx_free_on_early_error(appctx);
591
0
 out_free_spoe_appctx:
592
0
  pool_free(pool_head_spoe_appctx, spoe_appctx);
593
0
 out_error:
594
0
  send_log(&agent->fe, LOG_EMERG, "SPOE: [%s] failed to create SPOE applet\n", agent->id);
595
0
 out:
596
597
0
  return NULL;
598
0
}
599
600
/***************************************************************************
601
 * Functions that encode SPOE messages
602
 **************************************************************************/
603
/* Encode a SPOE message. If the next message can be processed, it returns 0. If
604
 * the message is too big, it returns -1.*/
605
static int spoe_encode_message(struct stream *s, struct spoe_context *ctx,
606
             struct spoe_message *msg, int dir,
607
             char **buf, char *end)
608
0
{
609
0
  struct sample   *smp;
610
0
  struct spoe_arg *arg;
611
0
  int ret;
612
613
0
  if (!acl_match_cond(msg->cond, s->be, s->sess, s, dir|SMP_OPT_FINAL))
614
0
    goto next;
615
616
  /* Check if there is enough space for the message name and the
617
   * number of arguments. It implies <msg->id_len> is encoded on 2
618
   * bytes, at most (< 2288). */
619
0
  if (*buf + 2 + msg->id_len + 1 > end)
620
0
    goto too_big;
621
622
  /* Encode the message name */
623
0
  if (spoe_encode_buffer(msg->id, msg->id_len, buf, end) == -1)
624
0
    goto too_big;
625
626
  /* Set the number of arguments for this message */
627
0
  **buf = msg->nargs;
628
0
  (*buf)++;
629
630
  /* Loop on arguments */
631
0
  list_for_each_entry(arg, &msg->args, list) {
632
    /* Encode the argument name as a string. It can by NULL */
633
0
    if (spoe_encode_buffer(arg->name, arg->name_len, buf, end) == -1)
634
0
      goto too_big;
635
636
    /* Fetch the argument value */
637
0
    smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
638
0
    ret = spoe_encode_data(smp, buf, end);
639
0
    if (ret == -1)
640
0
      goto too_big;
641
0
  }
642
643
0
  next:
644
0
  return 0;
645
646
0
  too_big:
647
0
  return -1;
648
0
}
649
650
/* Encode list of SPOE messages. On success it returns 1. If an error occurred, -1
651
 * is returned. If nothing has been encoded, it returns 0. */
652
static int spoe_encode_messages(struct stream *s, struct spoe_context *ctx,
653
        struct list *messages, int dir, int type)
654
0
{
655
0
  struct spoe_config  *conf = FLT_CONF(ctx->filter);
656
0
  struct spoe_agent   *agent = conf->agent;
657
0
  struct spoe_message *msg;
658
0
  char   *p, *start, *end;
659
660
0
  p   = b_head(&ctx->buffer);
661
0
  end =  p + agent->max_frame_size - SPOP_FRAME_HDR_SIZE;
662
663
  /* Set Frame type */
664
0
  *p++ = SPOP_FRM_T_HAPROXY_NOTIFY;
665
0
  start = p;
666
667
0
  if (type == SPOE_MSGS_BY_EVENT) { /* Loop on messages by event */
668
0
    list_for_each_entry(msg, messages, by_evt) {
669
0
      if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
670
0
        goto too_big;
671
0
    }
672
0
  }
673
0
  else if (type == SPOE_MSGS_BY_GROUP) { /* Loop on messages by group */
674
0
    list_for_each_entry(msg, messages, by_grp) {
675
0
    encode_grp_message:
676
0
      if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
677
0
        goto too_big;
678
0
    }
679
0
  }
680
0
  else
681
0
    goto skip;
682
683
684
  /* nothing has been encoded */
685
0
  if (p == start)
686
0
    goto skip;
687
688
0
  b_set_data(&ctx->buffer, p - b_head(&ctx->buffer));
689
0
  return 1;
690
691
0
  too_big:
692
  /* Return an error if nothing has been encoded because its too big */
693
0
  ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
694
0
  return -1;
695
696
0
  skip:
697
0
  return 0;
698
0
}
699
700
701
/***************************************************************************
702
 * Functions that handle SPOE actions
703
 **************************************************************************/
704
/* Helper function to set a variable */
705
static void spoe_set_var(struct spoe_context *ctx, char *scope, char *name, int len,
706
       struct sample *smp)
707
0
{
708
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
709
0
  struct spoe_agent  *agent = conf->agent;
710
0
  char                varname[64];
711
712
0
  memset(varname, 0, sizeof(varname));
713
0
  len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
714
0
           scope, agent->var_pfx, len, name);
715
0
  if (agent->flags & SPOE_FL_FORCE_SET_VAR)
716
0
    vars_set_by_name(varname, len, smp);
717
0
  else
718
0
    vars_set_by_name_ifexist(varname, len, smp);
719
0
}
720
721
/* Helper function to unset a variable */
722
static void spoe_unset_var(struct spoe_context *ctx, char *scope, char *name, int len,
723
         struct sample *smp)
724
0
{
725
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
726
0
  struct spoe_agent  *agent = conf->agent;
727
0
  char                varname[64];
728
729
0
  memset(varname, 0, sizeof(varname));
730
0
  len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
731
0
           scope, agent->var_pfx, len, name);
732
0
  vars_unset_by_name_ifexist(varname, len, smp);
733
0
}
734
735
736
static inline int spoe_decode_action_set_var(struct stream *s, struct spoe_context *ctx,
737
               char **buf, char *end, int dir)
738
0
{
739
0
  char         *str, *scope, *p = *buf;
740
0
  struct sample smp;
741
0
  uint64_t      sz;
742
0
  int           ret;
743
744
0
  if (p + 2 >= end)
745
0
    goto skip;
746
747
  /* SET-VAR requires 3 arguments */
748
0
  if (*p++ != 3)
749
0
    goto skip;
750
751
0
  switch (*p++) {
752
0
    case SPOP_SCOPE_PROC: scope = "proc"; break;
753
0
    case SPOP_SCOPE_SESS: scope = "sess"; break;
754
0
    case SPOP_SCOPE_TXN : scope = "txn";  break;
755
0
    case SPOP_SCOPE_REQ : scope = "req";  break;
756
0
    case SPOP_SCOPE_RES : scope = "res";  break;
757
0
    default: goto skip;
758
0
  }
759
760
0
  if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
761
0
    goto skip;
762
0
  memset(&smp, 0, sizeof(smp));
763
0
  smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
764
765
0
  if (spoe_decode_data(&p, end, &smp) == -1)
766
0
    goto skip;
767
768
0
  if (smp.data.type == SMP_T_ANY)
769
0
    spoe_unset_var(ctx, scope, str, sz, &smp);
770
0
  else
771
0
    spoe_set_var(ctx, scope, str, sz, &smp);
772
773
0
  ret  = (p - *buf);
774
0
  *buf = p;
775
0
  return ret;
776
0
  skip:
777
0
  return 0;
778
0
}
779
780
static inline int spoe_decode_action_unset_var(struct stream *s, struct spoe_context *ctx,
781
                 char **buf, char *end, int dir)
782
0
{
783
0
  char         *str, *scope, *p = *buf;
784
0
  struct sample smp;
785
0
  uint64_t      sz;
786
0
  int           ret;
787
788
0
  if (p + 2 >= end)
789
0
    goto skip;
790
791
  /* UNSET-VAR requires 2 arguments */
792
0
  if (*p++ != 2)
793
0
    goto skip;
794
795
0
  switch (*p++) {
796
0
    case SPOP_SCOPE_PROC: scope = "proc"; break;
797
0
    case SPOP_SCOPE_SESS: scope = "sess"; break;
798
0
    case SPOP_SCOPE_TXN : scope = "txn";  break;
799
0
    case SPOP_SCOPE_REQ : scope = "req";  break;
800
0
    case SPOP_SCOPE_RES : scope = "res";  break;
801
0
    default: goto skip;
802
0
  }
803
804
0
  if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
805
0
    goto skip;
806
0
  memset(&smp, 0, sizeof(smp));
807
0
  smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
808
809
0
  spoe_unset_var(ctx, scope, str, sz, &smp);
810
811
0
  ret  = (p - *buf);
812
0
  *buf = p;
813
0
  return ret;
814
0
  skip:
815
0
  return 0;
816
0
}
817
818
/* Process SPOE actions for a specific event. It returns 1 on success. If an
819
 * error occurred, 0 is returned. */
820
static int spoe_process_actions(struct stream *s, struct spoe_context *ctx, int dir)
821
0
{
822
0
  char *p, *end;
823
0
  int   ret;
824
825
0
  p   = b_head(&ctx->buffer);
826
0
  end = p + b_data(&ctx->buffer);
827
828
0
  while (p < end)  {
829
0
    enum spoe_action_type type;
830
831
0
    type = *p++;
832
0
    switch (type) {
833
0
      case SPOP_ACT_T_SET_VAR:
834
0
        ret = spoe_decode_action_set_var(s, ctx, &p, end, dir);
835
0
        if (!ret)
836
0
          goto skip;
837
0
        break;
838
839
0
      case SPOP_ACT_T_UNSET_VAR:
840
0
        ret = spoe_decode_action_unset_var(s, ctx, &p, end, dir);
841
0
        if (!ret)
842
0
          goto skip;
843
0
        break;
844
845
0
      default:
846
0
        goto skip;
847
0
    }
848
0
  }
849
850
0
  return 1;
851
0
  skip:
852
0
  return 0;
853
0
}
854
855
/***************************************************************************
856
 * Functions that process SPOE events
857
 **************************************************************************/
858
static inline enum spop_error spoe_ctx_err_to_spop_err(enum spoe_context_error err)
859
0
{
860
0
  switch (err) {
861
0
  case SPOE_CTX_ERR_NONE:
862
0
    return SPOP_ERR_NONE;
863
0
  case SPOE_CTX_ERR_TOUT:
864
0
    return SPOP_ERR_TOUT;
865
0
  case SPOE_CTX_ERR_RES:
866
0
    return SPOP_ERR_RES;
867
0
  case SPOE_CTX_ERR_TOO_BIG:
868
0
    return SPOP_ERR_TOO_BIG;
869
0
  case SPOE_CTX_ERR_INTERRUPT:
870
0
    return SPOP_ERR_IO;
871
0
  default:
872
0
    return SPOP_ERR_UNKNOWN;
873
0
  }
874
0
}
875
static void spoe_update_stats(struct stream *s, struct spoe_agent *agent,
876
            struct spoe_context *ctx, int dir)
877
0
{
878
0
  if (ctx->stats.start_ts != 0) {
879
0
    spoe_update_stat_time(&ctx->stats.start_ts, &ctx->stats.t_process);
880
0
    ctx->stats.t_total    += ctx->stats.t_process;
881
0
  }
882
883
0
  if (agent->var_t_process) {
884
0
    struct sample smp;
885
886
0
    memset(&smp, 0, sizeof(smp));
887
0
    smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
888
0
    smp.data.u.sint = ctx->stats.t_process;
889
0
    smp.data.type   = SMP_T_SINT;
890
891
0
    spoe_set_var(ctx, "txn", agent->var_t_process,
892
0
           strlen(agent->var_t_process), &smp);
893
0
  }
894
895
0
  if (agent->var_t_total) {
896
0
    struct sample smp;
897
898
0
    memset(&smp, 0, sizeof(smp));
899
0
    smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
900
0
    smp.data.u.sint = ctx->stats.t_total;
901
0
    smp.data.type   = SMP_T_SINT;
902
903
0
    spoe_set_var(ctx, "txn", agent->var_t_total,
904
0
           strlen(agent->var_t_total), &smp);
905
0
  }
906
0
}
907
908
static void spoe_handle_processing_error(struct stream *s, struct spoe_agent *agent,
909
           struct spoe_context *ctx, int dir)
910
0
{
911
0
  if (agent->var_on_error) {
912
0
    struct sample smp;
913
914
0
    memset(&smp, 0, sizeof(smp));
915
0
    smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
916
0
    smp.data.u.sint = ctx->status_code;
917
0
    smp.data.type   = SMP_T_BOOL;
918
919
0
    spoe_set_var(ctx, "txn", agent->var_on_error,
920
0
           strlen(agent->var_on_error), &smp);
921
0
  }
922
923
0
  ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
924
0
          ? SPOE_CTX_ST_READY
925
0
          : SPOE_CTX_ST_NONE);
926
0
}
927
928
static inline int spoe_start_processing(struct spoe_agent *agent, struct spoe_context *ctx, int dir)
929
0
{
930
  /* If a process is already started for this SPOE context, retry
931
   * later. */
932
0
  if (ctx->flags & SPOE_CTX_FL_PROCESS)
933
0
    return 0;
934
935
0
  ctx->stats.start_ts   = now_ns;
936
0
  ctx->stats.t_process  = -1;
937
938
0
  ctx->status_code = 0;
939
940
  /* Set the right flag to prevent request and response processing
941
   * in same time. */
942
0
  ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
943
0
           ? SPOE_CTX_FL_REQ_PROCESS
944
0
           : SPOE_CTX_FL_RSP_PROCESS);
945
0
  return 1;
946
0
}
947
948
static inline void spoe_stop_processing(struct spoe_agent *agent, struct spoe_context *ctx)
949
0
{
950
0
  struct spoe_appctx *sa = ctx->spoe_appctx;
951
952
0
  if (!(ctx->flags & SPOE_CTX_FL_PROCESS))
953
0
    return;
954
0
  _HA_ATOMIC_INC(&agent->counters.nb_processed);
955
0
  if (sa) {
956
0
    if (sa->status_code == SPOP_ERR_NONE)
957
0
      sa->status_code = spoe_ctx_err_to_spop_err(ctx->status_code);
958
0
    sa->spoe_ctx = NULL;
959
0
    appctx_strm(sa->owner)->parent = NULL;
960
0
    appctx_wakeup(sa->owner);
961
0
  }
962
963
  /* Reset the flag to allow next processing */
964
0
  ctx->flags &= ~SPOE_CTX_FL_PROCESS;
965
966
  /* Reset processing timer */
967
0
  ctx->process_exp = TICK_ETERNITY;
968
0
  ctx->strm->req.analyse_exp = TICK_ETERNITY;
969
0
  ctx->strm->res.analyse_exp = TICK_ETERNITY;
970
971
0
  spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
972
973
0
  ctx->spoe_appctx = NULL;
974
0
}
975
976
/* Process a list of SPOE messages. First, this functions will process messages
977
 *  and send them to an agent in a NOTIFY frame. Then, it will wait a ACK frame
978
 *  to process corresponding actions. During all the processing, it returns 0
979
 *  and it returns 1 when the processing is finished. If an error occurred, -1
980
 *  is returned. */
981
static int spoe_process_messages(struct stream *s, struct spoe_context *ctx,
982
         struct list *messages, int dir, int type)
983
0
{
984
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
985
0
  struct spoe_agent  *agent = conf->agent;
986
0
  int                 ret = 1;
987
988
0
  if (ctx->state == SPOE_CTX_ST_ERROR)
989
0
    goto end;
990
991
0
  if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
992
0
    ctx->status_code = SPOE_CTX_ERR_TOUT;
993
0
    goto end;
994
0
  }
995
996
0
  if (ctx->state == SPOE_CTX_ST_READY) {
997
0
    if (!tick_isset(ctx->process_exp)) {
998
0
      ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
999
0
      if (dir == SMP_OPT_DIR_REQ)
1000
0
        s->req.analyse_exp = ctx->process_exp;
1001
0
      else
1002
0
        s->res.analyse_exp = ctx->process_exp;
1003
0
    }
1004
0
    ret = spoe_start_processing(agent, ctx, dir);
1005
0
    if (!ret)
1006
0
      goto out;
1007
1008
0
    ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
1009
    /* fall through */
1010
0
  }
1011
1012
0
  if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
1013
0
    struct appctx *appctx;
1014
1015
0
    if (!spoe_acquire_buffer(&ctx->buffer, &ctx->buffer_wait))
1016
0
      goto out;
1017
0
    ret = spoe_encode_messages(s, ctx, messages, dir, type);
1018
0
    if (ret < 0)
1019
0
      goto end;
1020
0
    if (!ret)
1021
0
      goto skip;
1022
0
    appctx = spoe_create_appctx(ctx);
1023
0
    if (!appctx) {
1024
0
      ctx->status_code = SPOE_CTX_ERR_RES;
1025
0
      goto end;
1026
0
    }
1027
0
    ctx->state = SPOE_CTX_ST_SENDING_MSGS;
1028
0
  }
1029
1030
0
  if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
1031
0
    if (ctx->spoe_appctx)
1032
0
      appctx_wakeup(ctx->spoe_appctx->owner);
1033
0
    ret = 0;
1034
0
    goto out;
1035
0
  }
1036
1037
0
  if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
1038
0
    ret = 0;
1039
0
    goto out;
1040
0
  }
1041
1042
0
  if (ctx->state == SPOE_CTX_ST_DONE) {
1043
0
    spoe_process_actions(s, ctx, dir);
1044
0
    ret = 1;
1045
0
    ctx->frame_id++;
1046
0
    ctx->state = SPOE_CTX_ST_READY;
1047
0
    goto end;
1048
0
  }
1049
1050
0
  out:
1051
0
  return ret;
1052
1053
0
  skip:
1054
0
  ctx->stats.start_ts = 0;
1055
0
  ctx->state = SPOE_CTX_ST_READY;
1056
0
  spoe_stop_processing(agent, ctx);
1057
0
  return 1;
1058
1059
0
  end:
1060
0
  spoe_update_stats(s, agent, ctx, dir);
1061
0
  spoe_stop_processing(agent, ctx);
1062
0
  if (ctx->status_code) {
1063
0
    _HA_ATOMIC_INC(&agent->counters.nb_errors);
1064
0
    spoe_handle_processing_error(s, agent, ctx, dir);
1065
0
    ret = 1;
1066
0
  }
1067
0
  return ret;
1068
0
}
1069
1070
/* Process a SPOE group, ie the list of messages attached to the group <grp>.
1071
 * See spoe_process_message for details. */
1072
static int spoe_process_group(struct stream *s, struct spoe_context *ctx,
1073
            struct spoe_group *group, int dir)
1074
0
{
1075
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
1076
0
  struct spoe_agent  *agent = conf->agent;
1077
0
  int ret;
1078
1079
0
  if (LIST_ISEMPTY(&group->messages))
1080
0
    return 1;
1081
1082
0
  ret = spoe_process_messages(s, ctx, &group->messages, dir, SPOE_MSGS_BY_GROUP);
1083
0
  if (ret && ctx->stats.t_process != -1) {
1084
0
    if (ctx->status_code || !(agent->fe.options2 & PR_O2_NOLOGNORM))
1085
0
      send_log(&agent->fe, (!ctx->status_code ? LOG_NOTICE : LOG_WARNING),
1086
0
         "SPOE: [%s] <GROUP:%s> sid=%u st=%u %ld %llu/%llu\n",
1087
0
         agent->id, group->id, s->uniq_id, ctx->status_code, ctx->stats.t_process,
1088
0
         agent->counters.nb_errors, agent->counters.nb_processed);
1089
0
  }
1090
0
  return ret;
1091
0
}
1092
1093
/* Process a SPOE event, ie the list of messages attached to the event <ev>.
1094
 * See spoe_process_message for details. */
1095
static int spoe_process_event(struct stream *s, struct spoe_context *ctx,
1096
            enum spoe_event ev)
1097
0
{
1098
0
  struct spoe_config *conf = FLT_CONF(ctx->filter);
1099
0
  struct spoe_agent  *agent = conf->agent;
1100
0
  int dir, ret;
1101
1102
0
  dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
1103
1104
0
  if (LIST_ISEMPTY(&(ctx->events[ev])))
1105
0
    return 1;
1106
1107
0
  ret = spoe_process_messages(s, ctx, &(ctx->events[ev]), dir, SPOE_MSGS_BY_EVENT);
1108
0
  if (ret && ctx->stats.t_process != -1) {
1109
0
    if (ctx->status_code || !(agent->fe.options2 & PR_O2_NOLOGNORM))
1110
0
      send_log(&agent->fe, (!ctx->status_code ? LOG_NOTICE : LOG_WARNING),
1111
0
         "SPOE: [%s] <EVENT:%s> sid=%u st=%u %ld %llu/%llu\n",
1112
0
         agent->id, spoe_event_str[ev], s->uniq_id, ctx->status_code, ctx->stats.t_process,
1113
0
         agent->counters.nb_errors, agent->counters.nb_processed);
1114
0
  }
1115
0
  else if (ret == 0) {
1116
0
    if ((s->scf->flags & SC_FL_ERROR) ||
1117
0
        ((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1))) {
1118
0
      ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
1119
0
      spoe_stop_processing(agent, ctx);
1120
0
      spoe_handle_processing_error(s, agent, ctx, dir);
1121
0
      ret = 1;
1122
0
    }
1123
0
  }
1124
1125
0
  return ret;
1126
0
}
1127
1128
/***************************************************************************
1129
 * Functions that create/destroy SPOE contexts
1130
 **************************************************************************/
1131
static int spoe_acquire_buffer(struct buffer *buf, struct buffer_wait *buffer_wait)
1132
0
{
1133
0
  if (buf->size)
1134
0
    return 1;
1135
1136
0
  b_dequeue(buffer_wait);
1137
1138
0
  if (b_alloc(buf, DB_CHANNEL))
1139
0
    return 1;
1140
1141
0
  b_requeue(DB_CHANNEL, buffer_wait);
1142
0
  return 0;
1143
0
}
1144
1145
static void spoe_release_buffer(struct buffer *buf, struct buffer_wait *buffer_wait)
1146
0
{
1147
0
  b_dequeue(buffer_wait);
1148
1149
  /* Release the buffer if needed */
1150
0
  if (buf->size) {
1151
0
    b_free(buf);
1152
0
    offer_buffers(buffer_wait->target, 1);
1153
0
  }
1154
0
}
1155
1156
static int spoe_wakeup_context(struct spoe_context *ctx)
1157
0
{
1158
0
  task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1159
0
  return 1;
1160
0
}
1161
1162
static struct spoe_context *spoe_create_context(struct stream *s, struct filter *filter)
1163
0
{
1164
0
  struct spoe_config  *conf = FLT_CONF(filter);
1165
0
  struct spoe_context *ctx;
1166
1167
0
  ctx = pool_zalloc(pool_head_spoe_ctx);
1168
0
  if (ctx == NULL) {
1169
0
    return NULL;
1170
0
  }
1171
0
  ctx->filter      = filter;
1172
0
  ctx->state       = SPOE_CTX_ST_NONE;
1173
0
  ctx->status_code = SPOE_CTX_ERR_NONE;
1174
0
  ctx->flags       = 0;
1175
0
  ctx->events      = conf->agent->events;
1176
0
  ctx->groups      = &conf->agent->groups;
1177
0
  ctx->buffer      = BUF_NULL;
1178
0
  LIST_INIT(&ctx->buffer_wait.list);
1179
0
  ctx->buffer_wait.target = ctx;
1180
0
  ctx->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_context;
1181
1182
0
  ctx->stream_id   = 0;
1183
0
  ctx->frame_id    = 1;
1184
0
  ctx->process_exp = TICK_ETERNITY;
1185
1186
0
  ctx->stats.start_ts   =  0;
1187
0
  ctx->stats.t_process  = -1;
1188
0
  ctx->stats.t_total    =  0;
1189
1190
0
  ctx->strm   = s;
1191
0
  ctx->state  = SPOE_CTX_ST_READY;
1192
0
  filter->ctx = ctx;
1193
1194
0
  return ctx;
1195
0
}
1196
1197
static void spoe_destroy_context(struct filter *filter)
1198
0
{
1199
0
  struct spoe_config  *conf = FLT_CONF(filter);
1200
0
  struct spoe_context *ctx  = filter->ctx;
1201
1202
0
  if (!ctx)
1203
0
    return;
1204
1205
0
  if (ctx->state != SPOE_CTX_ST_NONE || ctx->state == SPOE_CTX_ST_READY) {
1206
0
    ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
1207
0
    _HA_ATOMIC_INC(&conf->agent->counters.nb_errors);
1208
0
  }
1209
0
  spoe_stop_processing(conf->agent, ctx);
1210
0
  pool_free(pool_head_spoe_ctx, ctx);
1211
0
  filter->ctx = NULL;
1212
0
}
1213
1214
static void spoe_reset_context(struct spoe_context *ctx)
1215
0
{
1216
0
  ctx->state  = SPOE_CTX_ST_READY;
1217
0
  ctx->flags &= ~SPOE_CTX_FL_PROCESS;
1218
1219
0
  ctx->stats.start_ts   =  0;
1220
0
  ctx->stats.t_process  = -1;
1221
0
  ctx->stats.t_total    =  0;
1222
0
}
1223
1224
1225
/***************************************************************************
1226
 * Hooks that manage the filter lifecycle (init/check/deinit)
1227
 **************************************************************************/
1228
/* Initialize the SPOE filter. Returns -1 on error, else 0. */
1229
static int spoe_init(struct proxy *px, struct flt_conf *fconf)
1230
0
{
1231
0
  struct spoe_config *conf = fconf->conf;
1232
1233
  /* conf->agent->fe was already initialized during the config
1234
   * parsing. Finish initialization. */
1235
0
  conf->agent->fe.mode = PR_MODE_SPOP;
1236
0
  conf->agent->fe.maxconn = 0;
1237
0
  conf->agent->fe.options2 |= PR_O2_INDEPSTR;
1238
0
  conf->agent->fe.conn_retries = CONN_RETRIES;
1239
0
  conf->agent->fe.accept = frontend_accept;
1240
0
  conf->agent->fe.srv = NULL;
1241
0
  conf->agent->fe.timeout.client = TICK_ETERNITY;
1242
0
  conf->agent->fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
1243
1244
0
  proxy_init_per_thr(&conf->agent->fe);
1245
1246
0
  conf->agent->engine_id = generate_pseudo_uuid();
1247
0
  if (conf->agent->engine_id == NULL)
1248
0
    return -1;
1249
1250
0
  fconf->flags |= FLT_CFG_FL_HTX;
1251
0
  return 0;
1252
0
}
1253
1254
/* Free resources allocated by the SPOE filter. */
1255
static void spoe_deinit(struct proxy *px, struct flt_conf *fconf)
1256
0
{
1257
0
  struct spoe_config *conf = fconf->conf;
1258
1259
0
  if (conf) {
1260
0
    struct spoe_agent *agent = conf->agent;
1261
1262
0
    spoe_release_agent(agent);
1263
0
    free(conf->id);
1264
0
    free(conf);
1265
0
  }
1266
0
  fconf->conf = NULL;
1267
0
}
1268
1269
/* Check configuration of a SPOE filter for a specified proxy.
1270
 * Return 1 on error, else 0. */
1271
static int spoe_check(struct proxy *px, struct flt_conf *fconf)
1272
0
{
1273
0
  struct flt_conf    *f;
1274
0
  struct spoe_config *conf = fconf->conf;
1275
0
  struct proxy       *target;
1276
1277
  /* Check all SPOE filters for proxy <px> to be sure all SPOE agent names
1278
   * are uniq */
1279
0
  list_for_each_entry(f, &px->filter_configs, list) {
1280
0
    struct spoe_config *c = f->conf;
1281
1282
    /* This is not an SPOE filter */
1283
0
    if (f->id != spoe_filter_id)
1284
0
      continue;
1285
    /* This is the current SPOE filter */
1286
0
    if (f == fconf)
1287
0
      continue;
1288
1289
    /* Check engine Id. It should be uniq */
1290
0
    if (strcmp(conf->id, c->id) == 0) {
1291
0
      ha_alert("Proxy %s : duplicated name for SPOE engine '%s'.\n",
1292
0
         px->id, conf->id);
1293
0
      return 1;
1294
0
    }
1295
0
  }
1296
1297
0
  target = proxy_be_by_name(conf->agent->b.name);
1298
0
  if (target == NULL) {
1299
0
    ha_alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
1300
0
       " declared at %s:%d.\n",
1301
0
       px->id, conf->agent->b.name, conf->agent->id,
1302
0
       conf->agent->conf.file, conf->agent->conf.line);
1303
0
    return 1;
1304
0
  }
1305
0
  if (target->mode == PR_MODE_TCP) {
1306
    /* Convert legacy SPOP backend by added the right mode */
1307
0
    target->mode = PR_MODE_SPOP;
1308
0
  }
1309
0
  if (target->mode != PR_MODE_SPOP) {
1310
0
    ha_alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
1311
0
       " at %s:%d must use SPOP mode.\n",
1312
0
       px->id, target->id, conf->agent->id,
1313
0
       conf->agent->conf.file, conf->agent->conf.line);
1314
0
    return 1;
1315
0
  }
1316
1317
0
  if (postresolve_logger_list(NULL, &conf->agent->fe.loggers, "SPOE agent", conf->agent->id) & ERR_CODE)
1318
0
    return 1;
1319
1320
0
  ha_free(&conf->agent->b.name);
1321
0
  conf->agent->b.be = target;
1322
0
  return 0;
1323
0
}
1324
1325
/**************************************************************************
1326
 * Hooks attached to a stream
1327
 *************************************************************************/
1328
/* Called when a filter instance is created and attach to a stream. It creates
1329
 * the context that will be used to process this stream. */
1330
static int spoe_start(struct stream *s, struct filter *filter)
1331
0
{
1332
0
  struct spoe_config  *conf  = FLT_CONF(filter);
1333
0
  struct spoe_agent   *agent = conf->agent;
1334
0
  struct spoe_context *ctx;
1335
1336
0
  if ((ctx = spoe_create_context(s, filter)) == NULL) {
1337
0
    send_log(&agent->fe, LOG_EMERG,
1338
0
       "SPOE: [%s] failed to create SPOE context\n",
1339
0
       agent->id);
1340
0
    return 0;
1341
0
  }
1342
1343
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_FE]))
1344
0
    filter->pre_analyzers |= AN_REQ_INSPECT_FE;
1345
1346
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_BE]))
1347
0
    filter->pre_analyzers |= AN_REQ_INSPECT_BE;
1348
1349
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_RSP]))
1350
0
    filter->pre_analyzers |= AN_RES_INSPECT;
1351
1352
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_FE]))
1353
0
    filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
1354
1355
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_BE]))
1356
0
    filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
1357
1358
0
  if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_RSP]))
1359
0
    filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
1360
1361
0
  return 1;
1362
0
}
1363
1364
/* Called when a filter instance is detached from a stream. It release the
1365
 * attached SPOE context. */
1366
static void spoe_stop(struct stream *s, struct filter *filter)
1367
0
{
1368
0
  spoe_destroy_context(filter);
1369
0
}
1370
1371
1372
/*
1373
 * Called when the stream is woken up because of expired timer.
1374
 */
1375
static void spoe_check_timeouts(struct stream *s, struct filter *filter)
1376
0
{
1377
0
  struct spoe_context *ctx = filter->ctx;
1378
1379
0
  if (tick_is_expired(ctx->process_exp, now_ms))
1380
0
    s->pending_events |= STRM_EVT_MSG;
1381
0
}
1382
1383
/* Called when we are ready to filter data on a channel */
1384
static int spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
1385
0
{
1386
0
  struct spoe_context *ctx = filter->ctx;
1387
0
  int                  ret = 1;
1388
1389
0
  if (ctx->state == SPOE_CTX_ST_NONE)
1390
0
    goto out;
1391
1392
0
  if (!(chn->flags & CF_ISRESP)) {
1393
0
    if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
1394
0
      chn->analysers |= AN_REQ_INSPECT_FE;
1395
0
    if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
1396
0
      chn->analysers |= AN_REQ_INSPECT_BE;
1397
1398
0
    if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
1399
0
      goto out;
1400
1401
0
    ctx->stream_id = s->uniq_id;
1402
0
    ret = spoe_process_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
1403
0
    if (!ret)
1404
0
      goto out;
1405
0
    ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
1406
0
  }
1407
0
  else {
1408
0
    if (filter->pre_analyzers & AN_RES_INSPECT)
1409
0
      chn->analysers |= AN_RES_INSPECT;
1410
1411
0
    if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
1412
0
      goto out;
1413
1414
0
    ret = spoe_process_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
1415
0
    if (!ret) {
1416
0
      channel_dont_read(chn);
1417
0
      channel_dont_close(chn);
1418
0
      goto out;
1419
0
    }
1420
0
    ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
1421
0
  }
1422
1423
0
  out:
1424
0
  return ret;
1425
0
}
1426
1427
/* Called before a processing happens on a given channel */
1428
static int spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
1429
        struct channel *chn, unsigned an_bit)
1430
0
{
1431
0
  struct spoe_context *ctx = filter->ctx;
1432
0
  int                  ret = 1;
1433
1434
0
  if (ctx->state == SPOE_CTX_ST_NONE)
1435
0
    goto out;
1436
1437
0
  switch (an_bit) {
1438
0
    case AN_REQ_INSPECT_FE:
1439
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
1440
0
      break;
1441
0
    case AN_REQ_INSPECT_BE:
1442
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
1443
0
      break;
1444
0
    case AN_RES_INSPECT:
1445
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_RSP);
1446
0
      break;
1447
0
    case AN_REQ_HTTP_PROCESS_FE:
1448
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
1449
0
      break;
1450
0
    case AN_REQ_HTTP_PROCESS_BE:
1451
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
1452
0
      break;
1453
0
    case AN_RES_HTTP_PROCESS_FE:
1454
0
      ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
1455
0
      break;
1456
0
  }
1457
1458
0
  out:
1459
0
  if (!ret && (chn->flags & CF_ISRESP)) {
1460
0
                channel_dont_read(chn);
1461
0
                channel_dont_close(chn);
1462
0
  }
1463
0
  return ret;
1464
0
}
1465
1466
/* Called when the filtering on the channel ends. */
1467
static int spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
1468
0
{
1469
0
  struct spoe_context *ctx = filter->ctx;
1470
1471
0
  if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
1472
0
    spoe_reset_context(ctx);
1473
0
  }
1474
1475
0
  return 1;
1476
0
}
1477
1478
/********************************************************************
1479
 * Functions that manage the filter initialization
1480
 ********************************************************************/
1481
struct flt_ops spoe_ops = {
1482
  /* Manage SPOE filter, called for each filter declaration */
1483
  .init   = spoe_init,
1484
  .deinit = spoe_deinit,
1485
  .check  = spoe_check,
1486
1487
  /* Handle start/stop of SPOE */
1488
  .attach         = spoe_start,
1489
  .detach         = spoe_stop,
1490
  .check_timeouts = spoe_check_timeouts,
1491
1492
  /* Handle channels activity */
1493
  .channel_start_analyze = spoe_start_analyze,
1494
  .channel_pre_analyze   = spoe_chn_pre_analyze,
1495
  .channel_end_analyze   = spoe_end_analyze,
1496
};
1497
1498
1499
static int cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
1500
0
{
1501
0
  const char *err;
1502
0
  int         i, err_code = 0;
1503
1504
0
  if ((cfg_scope == NULL && curengine != NULL) ||
1505
0
      (cfg_scope != NULL && curengine == NULL) ||
1506
0
      (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope) != 0))
1507
0
    goto out;
1508
1509
0
  if (strcmp(args[0], "spoe-agent") == 0) { /* new spoe-agent section */
1510
0
    if (!*args[1]) {
1511
0
      ha_alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
1512
0
         file, linenum);
1513
0
      err_code |= ERR_ALERT | ERR_ABORT;
1514
0
      goto out;
1515
0
    }
1516
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
1517
0
      err_code |= ERR_ABORT;
1518
0
      goto out;
1519
0
    }
1520
1521
0
    err = invalid_char(args[1]);
1522
0
    if (err) {
1523
0
      ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
1524
0
         file, linenum, *err, args[0], args[1]);
1525
0
      err_code |= ERR_ALERT | ERR_ABORT;
1526
0
      goto out;
1527
0
    }
1528
1529
0
    if (curagent != NULL) {
1530
0
      ha_alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
1531
0
         file, linenum);
1532
0
      err_code |= ERR_ALERT | ERR_ABORT;
1533
0
      goto out;
1534
0
    }
1535
0
    if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
1536
0
      ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1537
0
      err_code |= ERR_ALERT | ERR_ABORT;
1538
0
      goto out;
1539
0
    }
1540
1541
0
    curagent->id              = strdup(args[1]);
1542
1543
0
    curagent->conf.file       = strdup(file);
1544
0
    curagent->conf.line       = linenum;
1545
1546
0
    curagent->timeout.processing = TICK_ETERNITY;
1547
1548
0
    curagent->var_pfx        = NULL;
1549
0
    curagent->var_on_error   = NULL;
1550
0
    curagent->var_t_process  = NULL;
1551
0
    curagent->var_t_total    = NULL;
1552
0
    curagent->flags          = SPOE_FL_PIPELINING;
1553
0
    curagent->max_frame_size = SPOP_MAX_FRAME_SIZE;
1554
1555
0
    if ((curagent->events = calloc(SPOE_EV_EVENTS, sizeof(*curagent->events))) == NULL) {
1556
0
      ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1557
0
      err_code |= ERR_ALERT | ERR_ABORT;
1558
0
      goto out;
1559
0
    }
1560
1561
0
    for (i = 0; i < SPOE_EV_EVENTS; ++i)
1562
0
      LIST_INIT(&curagent->events[i]);
1563
0
    LIST_INIT(&curagent->groups);
1564
0
    LIST_INIT(&curagent->messages);
1565
0
  }
1566
0
  else if (strcmp(args[0], "use-backend") == 0) {
1567
0
    if (!*args[1]) {
1568
0
      ha_alert("parsing [%s:%d] : '%s' expects a backend name.\n",
1569
0
         file, linenum, args[0]);
1570
0
      err_code |= ERR_ALERT | ERR_FATAL;
1571
0
      goto out;
1572
0
    }
1573
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code))
1574
0
      goto out;
1575
0
    free(curagent->b.name);
1576
0
    curagent->b.name = strdup(args[1]);
1577
0
  }
1578
0
  else if (strcmp(args[0], "messages") == 0) {
1579
0
    int cur_arg = 1;
1580
0
    while (*args[cur_arg]) {
1581
0
      struct spoe_placeholder *ph = NULL;
1582
1583
0
      list_for_each_entry(ph, &curmphs, list) {
1584
0
        if (strcmp(ph->id, args[cur_arg]) == 0) {
1585
0
          ha_alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
1586
0
             file, linenum, args[cur_arg]);
1587
0
          err_code |= ERR_ALERT | ERR_FATAL;
1588
0
          goto out;
1589
0
        }
1590
0
      }
1591
1592
0
      if ((ph = calloc(1, sizeof(*ph))) == NULL) {
1593
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1594
0
        err_code |= ERR_ALERT | ERR_ABORT;
1595
0
        goto out;
1596
0
      }
1597
0
      ph->id = strdup(args[cur_arg]);
1598
0
      LIST_APPEND(&curmphs, &ph->list);
1599
0
      cur_arg++;
1600
0
    }
1601
0
  }
1602
0
  else if (strcmp(args[0], "groups") == 0) {
1603
0
    int cur_arg = 1;
1604
0
    while (*args[cur_arg]) {
1605
0
      struct spoe_placeholder *ph = NULL;
1606
1607
0
      list_for_each_entry(ph, &curgphs, list) {
1608
0
        if (strcmp(ph->id, args[cur_arg]) == 0) {
1609
0
          ha_alert("parsing [%s:%d]: spoe-group '%s' already used.\n",
1610
0
             file, linenum, args[cur_arg]);
1611
0
          err_code |= ERR_ALERT | ERR_FATAL;
1612
0
          goto out;
1613
0
        }
1614
0
      }
1615
1616
0
      if ((ph = calloc(1, sizeof(*ph))) == NULL) {
1617
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1618
0
        err_code |= ERR_ALERT | ERR_ABORT;
1619
0
        goto out;
1620
0
      }
1621
0
      ph->id = strdup(args[cur_arg]);
1622
0
      LIST_APPEND(&curgphs, &ph->list);
1623
0
      cur_arg++;
1624
0
    }
1625
0
  }
1626
0
  else if (strcmp(args[0], "timeout") == 0) {
1627
0
    unsigned int *tv = NULL;
1628
0
    const char   *res;
1629
0
    unsigned      timeout;
1630
1631
0
    if (!*args[1]) {
1632
0
      ha_alert("parsing [%s:%d] : 'timeout' expects 'hello', 'idle' and 'processing'.\n",
1633
0
         file, linenum);
1634
0
      err_code |= ERR_ALERT | ERR_FATAL;
1635
0
      goto out;
1636
0
    }
1637
0
    if (alertif_too_many_args(2, file, linenum, args, &err_code))
1638
0
      goto out;
1639
0
    if (strcmp(args[1], "hello") == 0) {
1640
      /* TODO: Add a warning or a diag ? Ignore it for now */
1641
0
      goto out;
1642
0
    }
1643
0
    else if (strcmp(args[1], "idle") == 0) {
1644
      /* TODO: Add a warning or a diag ? Ignore it for now */
1645
0
      goto out;
1646
0
    }
1647
0
    else if (strcmp(args[1], "processing") == 0)
1648
0
      tv = &curagent->timeout.processing;
1649
0
    else {
1650
0
      ha_alert("parsing [%s:%d] : 'timeout' supports 'processing' (got %s).\n",
1651
0
         file, linenum, args[1]);
1652
0
      err_code |= ERR_ALERT | ERR_FATAL;
1653
0
      goto out;
1654
0
    }
1655
0
    if (!*args[2]) {
1656
0
      ha_alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
1657
0
         file, linenum, args[1]);
1658
0
      err_code |= ERR_ALERT | ERR_FATAL;
1659
0
      goto out;
1660
0
    }
1661
0
    res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
1662
0
    if (res == PARSE_TIME_OVER) {
1663
0
      ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days).\n",
1664
0
         file, linenum, args[2], args[0], args[1]);
1665
0
      err_code |= ERR_ALERT | ERR_FATAL;
1666
0
      goto out;
1667
0
    }
1668
0
    else if (res == PARSE_TIME_UNDER) {
1669
0
      ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms.\n",
1670
0
         file, linenum, args[2], args[0], args[1]);
1671
0
      err_code |= ERR_ALERT | ERR_FATAL;
1672
0
      goto out;
1673
0
    }
1674
0
    else if (res) {
1675
0
      ha_alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
1676
0
         file, linenum, *res, args[1]);
1677
0
      err_code |= ERR_ALERT | ERR_FATAL;
1678
0
      goto out;
1679
0
    }
1680
0
    *tv = MS_TO_TICKS(timeout);
1681
0
  }
1682
0
  else if (strcmp(args[0], "option") == 0) {
1683
0
    if (!*args[1]) {
1684
0
                        ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
1685
0
         file, linenum, args[0]);
1686
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1687
0
                        goto out;
1688
0
                }
1689
1690
0
    if (strcmp(args[1], "pipelining") == 0) {
1691
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1692
0
        goto out;
1693
0
      if (kwm == 1)
1694
0
        curagent->flags &= ~SPOE_FL_PIPELINING;
1695
0
      else
1696
0
        curagent->flags |= SPOE_FL_PIPELINING;
1697
0
      goto out;
1698
0
    }
1699
0
    else if (strcmp(args[1], "async") == 0) {
1700
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1701
0
        goto out;
1702
      /* TODO: Add a warning or a diag ? Ignore it for now */
1703
0
      goto out;
1704
0
    }
1705
0
    else if (strcmp(args[1], "send-frag-payload") == 0) {
1706
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1707
0
        goto out;
1708
      /* TODO: Add a warning or a diag ? Ignore it for now */
1709
0
      goto out;
1710
0
    }
1711
0
    else if (strcmp(args[1], "dontlog-normal") == 0) {
1712
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1713
0
        goto out;
1714
0
      if (kwm == 1)
1715
0
        curpxopts2 &= ~PR_O2_NOLOGNORM;
1716
0
      else
1717
0
        curpxopts2 |= PR_O2_NOLOGNORM;
1718
0
      goto out;
1719
0
    }
1720
1721
    /* Following options does not support negation */
1722
0
    if (kwm == 1) {
1723
0
      ha_alert("parsing [%s:%d]: negation is not supported for option '%s'.\n",
1724
0
         file, linenum, args[1]);
1725
0
      err_code |= ERR_ALERT | ERR_FATAL;
1726
0
      goto out;
1727
0
    }
1728
1729
0
    if (strcmp(args[1], "var-prefix") == 0) {
1730
0
      char *tmp;
1731
1732
0
      if (!*args[2]) {
1733
0
        ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
1734
0
           file, linenum, args[0],
1735
0
           args[1]);
1736
0
        err_code |= ERR_ALERT | ERR_FATAL;
1737
0
        goto out;
1738
0
      }
1739
0
      if (alertif_too_many_args(2, file, linenum, args, &err_code))
1740
0
        goto out;
1741
0
      tmp = args[2];
1742
0
      while (*tmp) {
1743
0
        if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
1744
0
          ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
1745
0
             file, linenum, args[0], args[1]);
1746
0
          err_code |= ERR_ALERT | ERR_FATAL;
1747
0
          goto out;
1748
0
        }
1749
0
        tmp++;
1750
0
      }
1751
0
      curagent->var_pfx = strdup(args[2]);
1752
0
    }
1753
0
    else if (strcmp(args[1], "force-set-var") == 0) {
1754
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1755
0
        goto out;
1756
0
      curagent->flags |= SPOE_FL_FORCE_SET_VAR;
1757
0
    }
1758
0
    else if (strcmp(args[1], "continue-on-error") == 0) {
1759
0
      if (alertif_too_many_args(1, file, linenum, args, &err_code))
1760
0
        goto out;
1761
0
      curagent->flags |= SPOE_FL_CONT_ON_ERR;
1762
0
    }
1763
0
    else if (strcmp(args[1], "set-on-error") == 0) {
1764
0
      char *tmp;
1765
1766
0
      if (!*args[2]) {
1767
0
        ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
1768
0
           file, linenum, args[0],
1769
0
           args[1]);
1770
0
        err_code |= ERR_ALERT | ERR_FATAL;
1771
0
        goto out;
1772
0
      }
1773
0
      if (alertif_too_many_args(2, file, linenum, args, &err_code))
1774
0
        goto out;
1775
0
      tmp = args[2];
1776
0
      while (*tmp) {
1777
0
        if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
1778
0
          ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
1779
0
             file, linenum, args[0], args[1]);
1780
0
          err_code |= ERR_ALERT | ERR_FATAL;
1781
0
          goto out;
1782
0
        }
1783
0
        tmp++;
1784
0
      }
1785
0
      curagent->var_on_error = strdup(args[2]);
1786
0
    }
1787
0
    else if (strcmp(args[1], "set-process-time") == 0) {
1788
0
      char *tmp;
1789
1790
0
      if (!*args[2]) {
1791
0
        ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
1792
0
           file, linenum, args[0],
1793
0
           args[1]);
1794
0
        err_code |= ERR_ALERT | ERR_FATAL;
1795
0
        goto out;
1796
0
      }
1797
0
      if (alertif_too_many_args(2, file, linenum, args, &err_code))
1798
0
        goto out;
1799
0
      tmp = args[2];
1800
0
      while (*tmp) {
1801
0
        if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
1802
0
          ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
1803
0
             file, linenum, args[0], args[1]);
1804
0
          err_code |= ERR_ALERT | ERR_FATAL;
1805
0
          goto out;
1806
0
        }
1807
0
        tmp++;
1808
0
      }
1809
0
      curagent->var_t_process = strdup(args[2]);
1810
0
    }
1811
0
    else if (strcmp(args[1], "set-total-time") == 0) {
1812
0
      char *tmp;
1813
1814
0
      if (!*args[2]) {
1815
0
        ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
1816
0
           file, linenum, args[0],
1817
0
           args[1]);
1818
0
        err_code |= ERR_ALERT | ERR_FATAL;
1819
0
        goto out;
1820
0
      }
1821
0
      if (alertif_too_many_args(2, file, linenum, args, &err_code))
1822
0
        goto out;
1823
0
      tmp = args[2];
1824
0
      while (*tmp) {
1825
0
        if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
1826
0
          ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
1827
0
             file, linenum, args[0], args[1]);
1828
0
          err_code |= ERR_ALERT | ERR_FATAL;
1829
0
          goto out;
1830
0
        }
1831
0
        tmp++;
1832
0
      }
1833
0
      curagent->var_t_total = strdup(args[2]);
1834
0
    }
1835
0
    else {
1836
0
      ha_alert("parsing [%s:%d]: option '%s' is not supported.\n",
1837
0
         file, linenum, args[1]);
1838
0
      err_code |= ERR_ALERT | ERR_FATAL;
1839
0
      goto out;
1840
0
    }
1841
0
  }
1842
0
  else if (strcmp(args[0], "maxconnrate") == 0) {
1843
0
    if (!*args[1]) {
1844
0
      ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
1845
0
         file, linenum, args[0]);
1846
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1847
0
                        goto out;
1848
0
                }
1849
    /* TODO: Add a warning or a diag ? Ignore it for now */
1850
0
  }
1851
0
  else if (strcmp(args[0], "maxerrrate") == 0) {
1852
0
    if (!*args[1]) {
1853
0
      ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
1854
0
         file, linenum, args[0]);
1855
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1856
0
                        goto out;
1857
0
                }
1858
    /* TODO: Add a warning or a diag ? Ignore it for now */
1859
0
  }
1860
0
  else if (strcmp(args[0], "max-frame-size") == 0) {
1861
0
    if (!*args[1]) {
1862
0
      ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
1863
0
         file, linenum, args[0]);
1864
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1865
0
                        goto out;
1866
0
                }
1867
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code))
1868
0
      goto out;
1869
0
    curagent->max_frame_size = atol(args[1]);
1870
0
    if (curagent->max_frame_size < SPOP_MIN_FRAME_SIZE ||
1871
0
        curagent->max_frame_size > SPOP_MAX_FRAME_SIZE) {
1872
0
      ha_alert("parsing [%s:%d] : '%s' expects a positive integer argument in the range [%d, %d].\n",
1873
0
         file, linenum, args[0], SPOP_MIN_FRAME_SIZE, SPOP_MAX_FRAME_SIZE);
1874
0
      err_code |= ERR_ALERT | ERR_FATAL;
1875
0
      goto out;
1876
0
    }
1877
0
  }
1878
0
  else if (strcmp(args[0], "max-waiting-frames") == 0) {
1879
0
    if (!*args[1]) {
1880
0
      ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
1881
0
         file, linenum, args[0]);
1882
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1883
0
                        goto out;
1884
0
                }
1885
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code))
1886
0
      goto out;
1887
    /* TODO: Add a warning or a diag ? Ignore it for now */
1888
0
  }
1889
0
  else if (strcmp(args[0], "register-var-names") == 0) {
1890
0
    int   cur_arg;
1891
1892
0
    if (!*args[1]) {
1893
0
      ha_alert("parsing [%s:%d] : '%s' expects one or more variable names.\n",
1894
0
         file, linenum, args[0]);
1895
0
                        err_code |= ERR_ALERT | ERR_FATAL;
1896
0
                        goto out;
1897
0
                }
1898
0
    cur_arg = 1;
1899
0
    while (*args[cur_arg]) {
1900
0
      struct spoe_var_placeholder *vph;
1901
1902
0
      if ((vph = calloc(1, sizeof(*vph))) == NULL) {
1903
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1904
0
        err_code |= ERR_ALERT | ERR_ABORT;
1905
0
        goto out;
1906
0
      }
1907
0
      if ((vph->name  = strdup(args[cur_arg])) == NULL) {
1908
0
        free(vph);
1909
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1910
0
        err_code |= ERR_ALERT | ERR_ABORT;
1911
0
        goto out;
1912
0
      }
1913
0
      LIST_APPEND(&curvars, &vph->list);
1914
0
      cur_arg++;
1915
0
    }
1916
0
  }
1917
0
  else if (strcmp(args[0], "log") == 0) {
1918
0
    char *errmsg = NULL;
1919
1920
0
    if (!parse_logger(args, &curloggers, (kwm == 1), file, linenum, &errmsg)) {
1921
0
      ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
1922
0
      err_code |= ERR_ALERT | ERR_FATAL;
1923
0
      goto out;
1924
0
    }
1925
0
  }
1926
0
  else if (*args[0]) {
1927
0
    ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
1928
0
       file, linenum, args[0]);
1929
0
    err_code |= ERR_ALERT | ERR_FATAL;
1930
0
    goto out;
1931
0
  }
1932
0
 out:
1933
0
  return err_code;
1934
0
}
1935
static int cfg_parse_spoe_group(const char *file, int linenum, char **args, int kwm)
1936
0
{
1937
0
  struct spoe_group *grp;
1938
0
  const char        *err;
1939
0
  int                err_code = 0;
1940
1941
0
  if ((cfg_scope == NULL && curengine != NULL) ||
1942
0
      (cfg_scope != NULL && curengine == NULL) ||
1943
0
      (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope) != 0))
1944
0
    goto out;
1945
1946
0
  if (strcmp(args[0], "spoe-group") == 0) { /* new spoe-group section */
1947
0
    if (!*args[1]) {
1948
0
      ha_alert("parsing [%s:%d] : missing name for spoe-group section.\n",
1949
0
         file, linenum);
1950
0
      err_code |= ERR_ALERT | ERR_ABORT;
1951
0
      goto out;
1952
0
    }
1953
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
1954
0
      err_code |= ERR_ABORT;
1955
0
      goto out;
1956
0
    }
1957
1958
0
    err = invalid_char(args[1]);
1959
0
    if (err) {
1960
0
      ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
1961
0
         file, linenum, *err, args[0], args[1]);
1962
0
      err_code |= ERR_ALERT | ERR_ABORT;
1963
0
      goto out;
1964
0
    }
1965
1966
0
    list_for_each_entry(grp, &curgrps, list) {
1967
0
      if (strcmp(grp->id, args[1]) == 0) {
1968
0
        ha_alert("parsing [%s:%d]: spoe-group section '%s' has the same"
1969
0
           " name as another one declared at %s:%d.\n",
1970
0
           file, linenum, args[1], grp->conf.file, grp->conf.line);
1971
0
        err_code |= ERR_ALERT | ERR_FATAL;
1972
0
        goto out;
1973
0
      }
1974
0
    }
1975
1976
0
    if ((curgrp = calloc(1, sizeof(*curgrp))) == NULL) {
1977
0
      ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1978
0
      err_code |= ERR_ALERT | ERR_ABORT;
1979
0
      goto out;
1980
0
    }
1981
1982
0
    curgrp->id        = strdup(args[1]);
1983
0
    curgrp->conf.file = strdup(file);
1984
0
    curgrp->conf.line = linenum;
1985
0
    LIST_INIT(&curgrp->phs);
1986
0
    LIST_INIT(&curgrp->messages);
1987
0
    LIST_APPEND(&curgrps, &curgrp->list);
1988
0
  }
1989
0
  else if (strcmp(args[0], "messages") == 0) {
1990
0
    int cur_arg = 1;
1991
0
    while (*args[cur_arg]) {
1992
0
      struct spoe_placeholder *ph = NULL;
1993
1994
0
      list_for_each_entry(ph, &curgrp->phs, list) {
1995
0
        if (strcmp(ph->id, args[cur_arg]) == 0) {
1996
0
          ha_alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
1997
0
             file, linenum, args[cur_arg]);
1998
0
          err_code |= ERR_ALERT | ERR_FATAL;
1999
0
          goto out;
2000
0
        }
2001
0
      }
2002
2003
0
      if ((ph = calloc(1, sizeof(*ph))) == NULL) {
2004
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
2005
0
        err_code |= ERR_ALERT | ERR_ABORT;
2006
0
        goto out;
2007
0
      }
2008
0
      ph->id = strdup(args[cur_arg]);
2009
0
      LIST_APPEND(&curgrp->phs, &ph->list);
2010
0
      cur_arg++;
2011
0
    }
2012
0
  }
2013
0
  else if (*args[0]) {
2014
0
    ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-group section.\n",
2015
0
       file, linenum, args[0]);
2016
0
    err_code |= ERR_ALERT | ERR_FATAL;
2017
0
    goto out;
2018
0
  }
2019
0
 out:
2020
0
  return err_code;
2021
0
}
2022
2023
static int cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
2024
0
{
2025
0
  struct spoe_message *msg;
2026
0
  struct spoe_arg     *arg;
2027
0
  const char          *err;
2028
0
  char                *errmsg   = NULL;
2029
0
  int                  err_code = 0;
2030
2031
0
  if ((cfg_scope == NULL && curengine != NULL) ||
2032
0
      (cfg_scope != NULL && curengine == NULL) ||
2033
0
      (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope) != 0))
2034
0
    goto out;
2035
2036
0
  if (strcmp(args[0], "spoe-message") == 0) { /* new spoe-message section */
2037
0
    if (!*args[1]) {
2038
0
      ha_alert("parsing [%s:%d] : missing name for spoe-message section.\n",
2039
0
         file, linenum);
2040
0
      err_code |= ERR_ALERT | ERR_ABORT;
2041
0
      goto out;
2042
0
    }
2043
0
    if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
2044
0
      err_code |= ERR_ABORT;
2045
0
      goto out;
2046
0
    }
2047
2048
0
    err = invalid_char(args[1]);
2049
0
    if (err) {
2050
0
      ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
2051
0
         file, linenum, *err, args[0], args[1]);
2052
0
      err_code |= ERR_ALERT | ERR_ABORT;
2053
0
      goto out;
2054
0
    }
2055
2056
0
    list_for_each_entry(msg, &curmsgs, list) {
2057
0
      if (strcmp(msg->id, args[1]) == 0) {
2058
0
        ha_alert("parsing [%s:%d]: spoe-message section '%s' has the same"
2059
0
           " name as another one declared at %s:%d.\n",
2060
0
           file, linenum, args[1], msg->conf.file, msg->conf.line);
2061
0
        err_code |= ERR_ALERT | ERR_FATAL;
2062
0
        goto out;
2063
0
      }
2064
0
    }
2065
2066
0
    if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
2067
0
      ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
2068
0
      err_code |= ERR_ALERT | ERR_ABORT;
2069
0
      goto out;
2070
0
    }
2071
2072
0
    curmsg->id = strdup(args[1]);
2073
0
    curmsg->id_len = strlen(curmsg->id);
2074
0
    curmsg->event  = SPOE_EV_NONE;
2075
0
    curmsg->conf.file = strdup(file);
2076
0
    curmsg->conf.line = linenum;
2077
0
    curmsg->nargs = 0;
2078
0
    LIST_INIT(&curmsg->args);
2079
0
    LIST_INIT(&curmsg->acls);
2080
0
    LIST_INIT(&curmsg->by_evt);
2081
0
    LIST_INIT(&curmsg->by_grp);
2082
0
    LIST_APPEND(&curmsgs, &curmsg->list);
2083
0
  }
2084
0
  else if (strcmp(args[0], "args") == 0) {
2085
0
    int cur_arg = 1;
2086
2087
0
    curproxy->conf.args.ctx  = ARGC_SPOE;
2088
0
    curproxy->conf.args.file = file;
2089
0
    curproxy->conf.args.line = linenum;
2090
0
    while (*args[cur_arg]) {
2091
0
      char *delim = strchr(args[cur_arg], '=');
2092
0
      int   idx = 0;
2093
2094
0
      if ((arg = calloc(1, sizeof(*arg))) == NULL) {
2095
0
        ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
2096
0
        err_code |= ERR_ALERT | ERR_ABORT;
2097
0
        goto out;
2098
0
      }
2099
2100
0
      if (!delim) {
2101
0
        arg->name = NULL;
2102
0
        arg->name_len  = 0;
2103
0
        delim = args[cur_arg];
2104
0
      }
2105
0
      else {
2106
0
        arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
2107
0
        arg->name_len = delim - args[cur_arg];
2108
0
        delim++;
2109
0
      }
2110
0
      arg->expr = sample_parse_expr((char*[]){delim, NULL},
2111
0
                  &idx, file, linenum, &errmsg,
2112
0
                  &curproxy->conf.args, NULL);
2113
0
      if (arg->expr == NULL) {
2114
0
        ha_alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
2115
0
        err_code |= ERR_ALERT | ERR_FATAL;
2116
0
        free(arg->name);
2117
0
        free(arg);
2118
0
        goto out;
2119
0
      }
2120
0
      curmsg->nargs++;
2121
0
      LIST_APPEND(&curmsg->args, &arg->list);
2122
0
      cur_arg++;
2123
0
    }
2124
0
    curproxy->conf.args.file = NULL;
2125
0
    curproxy->conf.args.line = 0;
2126
0
  }
2127
0
  else if (strcmp(args[0], "acl") == 0) {
2128
0
    err = invalid_char(args[1]);
2129
0
    if (err) {
2130
0
      ha_alert("parsing [%s:%d] : character '%c' is not permitted in acl name '%s'.\n",
2131
0
         file, linenum, *err, args[1]);
2132
0
      err_code |= ERR_ALERT | ERR_FATAL;
2133
0
      goto out;
2134
0
    }
2135
0
    if (strcasecmp(args[1], "or") == 0) {
2136
0
      ha_alert("parsing [%s:%d] : acl name '%s' will never match. 'or' is used to express a "
2137
0
           "logical disjunction within a condition.\n",
2138
0
           file, linenum, args[1]);
2139
0
      err_code |= ERR_ALERT | ERR_FATAL;
2140
0
      goto out;
2141
0
    }
2142
0
    if (parse_acl((const char **)args + 1, &curmsg->acls, &errmsg, &curproxy->conf.args, file, linenum) == NULL) {
2143
0
      ha_alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
2144
0
         file, linenum, args[1], errmsg);
2145
0
      err_code |= ERR_ALERT | ERR_FATAL;
2146
0
      goto out;
2147
0
    }
2148
0
  }
2149
0
  else if (strcmp(args[0], "event") == 0) {
2150
0
    if (!*args[1]) {
2151
0
      ha_alert("parsing [%s:%d] : missing event name.\n", file, linenum);
2152
0
      err_code |= ERR_ALERT | ERR_FATAL;
2153
0
      goto out;
2154
0
    }
2155
    /* if (alertif_too_many_args(1, file, linenum, args, &err_code)) */
2156
    /*  goto out; */
2157
2158
0
    if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]) == 0)
2159
0
      curmsg->event = SPOE_EV_ON_CLIENT_SESS;
2160
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]) == 0)
2161
0
      curmsg->event = SPOE_EV_ON_SERVER_SESS;
2162
2163
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]) == 0)
2164
0
      curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
2165
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]) == 0)
2166
0
      curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
2167
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]) == 0)
2168
0
      curmsg->event = SPOE_EV_ON_TCP_RSP;
2169
2170
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]) == 0)
2171
0
      curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
2172
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]) == 0)
2173
0
      curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
2174
0
    else if (strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]) == 0)
2175
0
      curmsg->event = SPOE_EV_ON_HTTP_RSP;
2176
0
    else {
2177
0
      ha_alert("parsing [%s:%d] : unknown event '%s'.\n",
2178
0
         file, linenum, args[1]);
2179
0
      err_code |= ERR_ALERT | ERR_FATAL;
2180
0
      goto out;
2181
0
    }
2182
2183
0
    if (strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0) {
2184
0
      struct acl_cond *cond;
2185
2186
0
      cond = build_acl_cond(file, linenum, &curmsg->acls,
2187
0
                curproxy, (const char **)args+2,
2188
0
                &errmsg);
2189
0
      if (cond == NULL) {
2190
0
        ha_alert("parsing [%s:%d] : error detected while "
2191
0
           "parsing an 'event %s' condition : %s.\n",
2192
0
           file, linenum, args[1], errmsg);
2193
0
        err_code |= ERR_ALERT | ERR_FATAL;
2194
0
        goto out;
2195
0
      }
2196
0
      curmsg->cond = cond;
2197
0
    }
2198
0
    else if (*args[2]) {
2199
0
      ha_alert("parsing [%s:%d]: 'event %s' expects either 'if' "
2200
0
         "or 'unless' followed by a condition but found '%s'.\n",
2201
0
         file, linenum, args[1], args[2]);
2202
0
      err_code |= ERR_ALERT | ERR_FATAL;
2203
0
      goto out;
2204
0
    }
2205
0
  }
2206
0
  else if (!*args[0]) {
2207
0
    ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
2208
0
       file, linenum, args[0]);
2209
0
    err_code |= ERR_ALERT | ERR_FATAL;
2210
0
    goto out;
2211
0
  }
2212
0
 out:
2213
0
  free(errmsg);
2214
0
  return err_code;
2215
0
}
2216
2217
/* Return -1 on error, else 0 */
2218
static int parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
2219
        struct flt_conf *fconf, char **err, void *private)
2220
0
{
2221
0
  struct list backup_sections;
2222
0
  struct spoe_config          *conf;
2223
0
  struct spoe_message         *msg, *msgback;
2224
0
  struct spoe_group           *grp, *grpback;
2225
0
  struct spoe_placeholder     *ph, *phback;
2226
0
  struct spoe_var_placeholder *vph, *vphback;
2227
0
  struct cfgfile        cfg_file = { };
2228
0
  struct logger               *logger, *loggerback;
2229
0
  char                        *file = NULL, *engine = NULL;
2230
0
  int                          ret, pos = *cur_arg + 1;
2231
2232
0
  LIST_INIT(&curmsgs);
2233
0
  LIST_INIT(&curgrps);
2234
0
  LIST_INIT(&curmphs);
2235
0
  LIST_INIT(&curgphs);
2236
0
  LIST_INIT(&curvars);
2237
0
  LIST_INIT(&curloggers);
2238
0
  curpxopts  = 0;
2239
0
  curpxopts2 = 0;
2240
2241
0
  conf = calloc(1, sizeof(*conf));
2242
0
  if (conf == NULL) {
2243
0
    memprintf(err, "%s: out of memory", args[*cur_arg]);
2244
0
    goto error;
2245
0
  }
2246
0
  conf->proxy = px;
2247
2248
0
  while (*args[pos]) {
2249
0
    if (strcmp(args[pos], "config") == 0) {
2250
0
      if (!*args[pos+1]) {
2251
0
        memprintf(err, "'%s' : '%s' option without value",
2252
0
            args[*cur_arg], args[pos]);
2253
0
        goto error;
2254
0
      }
2255
0
      file = args[pos+1];
2256
0
      pos += 2;
2257
0
    }
2258
0
    else if (strcmp(args[pos], "engine") == 0) {
2259
0
      if (!*args[pos+1]) {
2260
0
        memprintf(err, "'%s' : '%s' option without value",
2261
0
            args[*cur_arg], args[pos]);
2262
0
        goto error;
2263
0
      }
2264
0
      engine = args[pos+1];
2265
0
      pos += 2;
2266
0
    }
2267
0
    else {
2268
0
      memprintf(err, "unknown keyword '%s'", args[pos]);
2269
0
      goto error;
2270
0
    }
2271
0
  }
2272
0
  if (file == NULL) {
2273
0
    memprintf(err, "'%s' : missing config file", args[*cur_arg]);
2274
0
    goto error;
2275
0
  }
2276
2277
  /* backup sections and register SPOE sections */
2278
0
  LIST_INIT(&backup_sections);
2279
0
  cfg_backup_sections(&backup_sections);
2280
0
  cfg_register_section("spoe-agent",   cfg_parse_spoe_agent, NULL);
2281
0
  cfg_register_section("spoe-group",   cfg_parse_spoe_group, NULL);
2282
0
  cfg_register_section("spoe-message", cfg_parse_spoe_message, NULL);
2283
2284
  /* Parse SPOE filter configuration file */
2285
0
  BUG_ON(px != curproxy);
2286
0
  curengine = engine;
2287
0
  curagent  = NULL;
2288
0
  curmsg    = NULL;
2289
2290
  /* load the content of SPOE config file from cfg_file.filename into some
2291
   * area in .heap. readcfgfile() now parses the content of config files
2292
   * stored in RAM as separate chunks (see struct cfgfile in cfgparse.h),
2293
   * these chunks chained in cfg_cfgfiles global list.
2294
   */
2295
0
  cfg_file.filename = file;
2296
0
  cfg_file.size = load_cfg_in_mem(file, &cfg_file.content);
2297
0
  if (cfg_file.size < 0) {
2298
0
    goto error;
2299
0
  }
2300
0
  ret = parse_cfg(&cfg_file);
2301
0
  ha_free(&cfg_file.content);
2302
2303
  /* unregister SPOE sections and restore previous sections */
2304
0
  cfg_unregister_sections();
2305
0
  cfg_restore_sections(&backup_sections);
2306
2307
0
  if (ret == -1) {
2308
0
    memprintf(err, "Could not open configuration file %s : %s",
2309
0
        file, strerror(errno));
2310
0
    goto error;
2311
0
  }
2312
0
  if (ret & (ERR_ABORT|ERR_FATAL)) {
2313
0
    memprintf(err, "Error(s) found in configuration file %s", file);
2314
0
    goto error;
2315
0
  }
2316
2317
  /* Check SPOE agent */
2318
0
  if (curagent == NULL) {
2319
0
    memprintf(err, "No SPOE agent found in file %s", file);
2320
0
    goto error;
2321
0
  }
2322
0
  if (curagent->b.name == NULL) {
2323
0
    memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
2324
0
        curagent->id, curagent->conf.file, curagent->conf.line);
2325
0
    goto error;
2326
0
  }
2327
0
  if (curagent->timeout.processing == TICK_ETERNITY) {
2328
0
    ha_warning("Proxy '%s': missing 'processing' timeout for SPOE agent '%s' declare at %s:%d.\n"
2329
0
         "   | While not properly invalid, you will certainly encounter various problems\n"
2330
0
         "   | with such a configuration. To fix this, please ensure it is set to a non-zero value.\n",
2331
0
         px->id, curagent->id, curagent->conf.file, curagent->conf.line);
2332
0
  }
2333
0
  if (curagent->var_pfx == NULL) {
2334
0
    char *tmp = curagent->id;
2335
2336
0
    while (*tmp) {
2337
0
      if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
2338
0
        memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
2339
0
            "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
2340
0
            curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
2341
0
        goto error;
2342
0
      }
2343
0
      tmp++;
2344
0
    }
2345
0
    curagent->var_pfx = strdup(curagent->id);
2346
0
  }
2347
2348
0
  if (curagent->var_on_error) {
2349
0
    struct arg arg;
2350
2351
0
    trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
2352
0
             curagent->var_pfx, curagent->var_on_error);
2353
2354
0
    arg.type = ARGT_STR;
2355
0
    arg.data.str.area = trash.area;
2356
0
    arg.data.str.data = trash.data;
2357
0
    arg.data.str.size = 0; /* Set it to 0 to not release it in vars_check_arg() */
2358
0
    if (!vars_check_arg(&arg, err)) {
2359
0
      memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
2360
0
          curagent->id, curagent->var_pfx, curagent->var_on_error, *err);
2361
0
      goto error;
2362
0
    }
2363
0
  }
2364
2365
0
  if (curagent->var_t_process) {
2366
0
    struct arg arg;
2367
2368
0
    trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
2369
0
             curagent->var_pfx, curagent->var_t_process);
2370
2371
0
    arg.type = ARGT_STR;
2372
0
    arg.data.str.area = trash.area;
2373
0
    arg.data.str.data = trash.data;
2374
0
    arg.data.str.size = 0;  /* Set it to 0 to not release it in vars_check_arg() */
2375
0
    if (!vars_check_arg(&arg, err)) {
2376
0
      memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
2377
0
          curagent->id, curagent->var_pfx, curagent->var_t_process, *err);
2378
0
      goto error;
2379
0
    }
2380
0
  }
2381
2382
0
  if (curagent->var_t_total) {
2383
0
    struct arg arg;
2384
2385
0
    trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
2386
0
             curagent->var_pfx, curagent->var_t_total);
2387
2388
0
    arg.type = ARGT_STR;
2389
0
    arg.data.str.area = trash.area;
2390
0
    arg.data.str.data = trash.data;
2391
0
    arg.data.str.size = 0;  /* Set it to 0 to not release it in vars_check_arg() */
2392
0
    if (!vars_check_arg(&arg, err)) {
2393
0
      memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
2394
0
          curagent->id, curagent->var_pfx, curagent->var_t_process, *err);
2395
0
      goto error;
2396
0
    }
2397
0
  }
2398
2399
0
  if (LIST_ISEMPTY(&curmphs) && LIST_ISEMPTY(&curgphs)) {
2400
0
    ha_warning("Proxy '%s': No message/group used by SPOE agent '%s' declared at %s:%d.\n",
2401
0
         px->id, curagent->id, curagent->conf.file, curagent->conf.line);
2402
0
    goto finish;
2403
0
  }
2404
2405
  /* Replace placeholders by the corresponding messages for the SPOE
2406
   * agent */
2407
0
  list_for_each_entry(ph, &curmphs, list) {
2408
0
    list_for_each_entry(msg, &curmsgs, list) {
2409
0
      struct spoe_arg *arg;
2410
0
      unsigned int     where;
2411
2412
0
      if (strcmp(msg->id, ph->id) == 0) {
2413
0
        if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
2414
0
          if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
2415
0
            msg->event = SPOE_EV_ON_TCP_REQ_FE;
2416
0
          if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
2417
0
            msg->event = SPOE_EV_ON_HTTP_REQ_FE;
2418
0
        }
2419
0
        if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
2420
0
                     msg->event == SPOE_EV_ON_TCP_REQ_FE ||
2421
0
                     msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
2422
0
          ha_warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
2423
0
               px->id, msg->conf.file, msg->conf.line);
2424
0
          goto next_mph;
2425
0
        }
2426
0
        if (msg->event == SPOE_EV_NONE) {
2427
0
          ha_warning("Proxy '%s': Ignore SPOE message '%s' without event at %s:%d.\n",
2428
0
               px->id, msg->id, msg->conf.file, msg->conf.line);
2429
0
          goto next_mph;
2430
0
        }
2431
2432
0
        where = 0;
2433
0
        switch (msg->event) {
2434
0
          case SPOE_EV_ON_CLIENT_SESS:
2435
0
            where |= SMP_VAL_FE_CON_ACC;
2436
0
            break;
2437
2438
0
          case SPOE_EV_ON_TCP_REQ_FE:
2439
0
            where |= SMP_VAL_FE_REQ_CNT;
2440
0
            break;
2441
2442
0
          case SPOE_EV_ON_HTTP_REQ_FE:
2443
0
            where |= SMP_VAL_FE_HRQ_HDR;
2444
0
            break;
2445
2446
0
          case SPOE_EV_ON_TCP_REQ_BE:
2447
0
            if (px->cap & PR_CAP_FE)
2448
0
              where |= SMP_VAL_FE_REQ_CNT;
2449
0
            if (px->cap & PR_CAP_BE)
2450
0
              where |= SMP_VAL_BE_REQ_CNT;
2451
0
            break;
2452
2453
0
          case SPOE_EV_ON_HTTP_REQ_BE:
2454
0
            if (px->cap & PR_CAP_FE)
2455
0
              where |= SMP_VAL_FE_HRQ_HDR;
2456
0
            if (px->cap & PR_CAP_BE)
2457
0
              where |= SMP_VAL_BE_HRQ_HDR;
2458
0
            break;
2459
2460
0
          case SPOE_EV_ON_SERVER_SESS:
2461
0
            where |= SMP_VAL_BE_SRV_CON;
2462
0
            break;
2463
2464
0
          case SPOE_EV_ON_TCP_RSP:
2465
0
            if (px->cap & PR_CAP_FE)
2466
0
              where |= SMP_VAL_FE_RES_CNT;
2467
0
            if (px->cap & PR_CAP_BE)
2468
0
              where |= SMP_VAL_BE_RES_CNT;
2469
0
            break;
2470
2471
0
          case SPOE_EV_ON_HTTP_RSP:
2472
0
            if (px->cap & PR_CAP_FE)
2473
0
              where |= SMP_VAL_FE_HRS_HDR;
2474
0
            if (px->cap & PR_CAP_BE)
2475
0
              where |= SMP_VAL_BE_HRS_HDR;
2476
0
            break;
2477
2478
0
          default:
2479
0
            break;
2480
0
        }
2481
2482
0
        list_for_each_entry(arg, &msg->args, list) {
2483
0
          if (!(arg->expr->fetch->val & where)) {
2484
0
            memprintf(err, "Ignore SPOE message '%s' at %s:%d: "
2485
0
              "some args extract information from '%s', "
2486
0
              "none of which is available here ('%s')",
2487
0
              msg->id, msg->conf.file, msg->conf.line,
2488
0
              sample_ckp_names(arg->expr->fetch->use),
2489
0
              sample_ckp_names(where));
2490
0
            goto error;
2491
0
          }
2492
0
        }
2493
2494
0
        msg->agent = curagent;
2495
0
        LIST_APPEND(&curagent->events[msg->event], &msg->by_evt);
2496
0
        goto next_mph;
2497
0
      }
2498
0
    }
2499
0
    memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
2500
0
        curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
2501
0
    goto error;
2502
0
    next_mph:
2503
0
    continue;
2504
0
  }
2505
2506
  /* Replace placeholders by the corresponding groups for the SPOE
2507
   * agent */
2508
0
  list_for_each_entry(ph, &curgphs, list) {
2509
0
    list_for_each_entry_safe(grp, grpback, &curgrps, list) {
2510
0
      if (strcmp(grp->id, ph->id) == 0) {
2511
0
        grp->agent = curagent;
2512
0
        LIST_DELETE(&grp->list);
2513
0
        LIST_APPEND(&curagent->groups, &grp->list);
2514
0
        goto next_aph;
2515
0
      }
2516
0
    }
2517
0
    memprintf(err, "SPOE agent '%s' try to use undefined SPOE group '%s' at %s:%d",
2518
0
        curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
2519
0
    goto error;
2520
0
    next_aph:
2521
0
    continue;
2522
0
  }
2523
2524
  /* Replace placeholders by the corresponding message for each SPOE
2525
   * group of the SPOE agent */
2526
0
  list_for_each_entry(grp, &curagent->groups, list) {
2527
0
    list_for_each_entry_safe(ph, phback, &grp->phs, list) {
2528
0
      list_for_each_entry(msg, &curmsgs, list) {
2529
0
        if (strcmp(msg->id, ph->id) == 0) {
2530
0
          if (msg->group != NULL) {
2531
0
            memprintf(err, "SPOE message '%s' already belongs to "
2532
0
                "the SPOE group '%s' declare at %s:%d",
2533
0
                msg->id, msg->group->id,
2534
0
                msg->group->conf.file,
2535
0
                msg->group->conf.line);
2536
0
            goto error;
2537
0
          }
2538
2539
          /* Scope for arguments are not checked for now. We will check
2540
           * them only if a rule use the corresponding SPOE group. */
2541
0
          msg->agent = curagent;
2542
0
          msg->group = grp;
2543
0
          LIST_DELETE(&ph->list);
2544
0
          LIST_APPEND(&grp->messages, &msg->by_grp);
2545
0
          goto next_mph_grp;
2546
0
        }
2547
0
      }
2548
0
      memprintf(err, "SPOE group '%s' try to use undefined SPOE message '%s' at %s:%d",
2549
0
          grp->id, ph->id, curagent->conf.file, curagent->conf.line);
2550
0
      goto error;
2551
0
      next_mph_grp:
2552
0
      continue;
2553
0
    }
2554
0
  }
2555
2556
0
 finish:
2557
  /* move curmsgs to the agent message list */
2558
0
  curmsgs.n->p = &curagent->messages;
2559
0
  curmsgs.p->n = &curagent->messages;
2560
0
  curagent->messages = curmsgs;
2561
0
  LIST_INIT(&curmsgs);
2562
2563
0
  conf->id    = strdup(engine ? engine : curagent->id);
2564
0
  conf->agent = curagent;
2565
0
  curagent->spoe_conf = conf;
2566
2567
  /* Start agent's proxy initialization here. It will be finished during
2568
   * the filter init. */
2569
0
        memset(&conf->agent->fe, 0, sizeof(conf->agent->fe));
2570
0
  if (!setup_new_proxy(&conf->agent->fe, conf->agent->id, PR_CAP_FE | PR_CAP_INT, err)) {
2571
0
    memprintf(err, "SPOE agent '%s': %s",
2572
0
        curagent->id, *err);
2573
0
    goto error;
2574
0
  }
2575
0
  conf->agent->fe.parent    = conf->agent;
2576
0
  conf->agent->fe.options  |= curpxopts;
2577
0
  conf->agent->fe.options2 |= curpxopts2;
2578
2579
0
  list_for_each_entry_safe(logger, loggerback, &curloggers, list) {
2580
0
    LIST_DELETE(&logger->list);
2581
0
    LIST_APPEND(&conf->agent->fe.loggers, &logger->list);
2582
0
  }
2583
2584
0
  list_for_each_entry_safe(ph, phback, &curmphs, list) {
2585
0
    LIST_DELETE(&ph->list);
2586
0
    spoe_release_placeholder(ph);
2587
0
  }
2588
0
  list_for_each_entry_safe(ph, phback, &curgphs, list) {
2589
0
    LIST_DELETE(&ph->list);
2590
0
    spoe_release_placeholder(ph);
2591
0
  }
2592
0
  list_for_each_entry_safe(vph, vphback, &curvars, list) {
2593
0
    struct arg arg;
2594
2595
0
    trash.data = snprintf(trash.area, trash.size, "proc.%s.%s",
2596
0
             curagent->var_pfx, vph->name);
2597
2598
0
    arg.type = ARGT_STR;
2599
0
    arg.data.str.area = trash.area;
2600
0
    arg.data.str.data = trash.data;
2601
0
    arg.data.str.size = 0;  /* Set it to 0 to not release it in vars_check_arg() */
2602
0
    if (!vars_check_arg(&arg, err)) {
2603
0
      memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
2604
0
          curagent->id, curagent->var_pfx, vph->name, *err);
2605
0
      goto error;
2606
0
    }
2607
2608
0
    LIST_DELETE(&vph->list);
2609
0
    free(vph->name);
2610
0
    free(vph);
2611
0
  }
2612
0
  list_for_each_entry_safe(grp, grpback, &curgrps, list) {
2613
0
    LIST_DELETE(&grp->list);
2614
0
    spoe_release_group(grp);
2615
0
  }
2616
0
  *cur_arg    = pos;
2617
0
  fconf->id   = spoe_filter_id;
2618
0
  fconf->ops  = &spoe_ops;
2619
0
  fconf->conf = conf;
2620
0
  return 0;
2621
2622
0
 error:
2623
0
  ha_free(&cfg_file.content);
2624
0
  spoe_release_agent(curagent);
2625
0
  list_for_each_entry_safe(ph, phback, &curmphs, list) {
2626
0
    LIST_DELETE(&ph->list);
2627
0
    spoe_release_placeholder(ph);
2628
0
  }
2629
0
  list_for_each_entry_safe(ph, phback, &curgphs, list) {
2630
0
    LIST_DELETE(&ph->list);
2631
0
    spoe_release_placeholder(ph);
2632
0
  }
2633
0
  list_for_each_entry_safe(vph, vphback, &curvars, list) {
2634
0
    LIST_DELETE(&vph->list);
2635
0
    free(vph->name);
2636
0
    free(vph);
2637
0
  }
2638
0
  list_for_each_entry_safe(grp, grpback, &curgrps, list) {
2639
0
    LIST_DELETE(&grp->list);
2640
0
    spoe_release_group(grp);
2641
0
  }
2642
0
  list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
2643
0
    LIST_DELETE(&msg->list);
2644
0
    spoe_release_message(msg);
2645
0
  }
2646
0
  list_for_each_entry_safe(logger, loggerback, &curloggers, list) {
2647
0
    LIST_DELETE(&logger->list);
2648
0
    free(logger);
2649
0
  }
2650
0
  free(conf);
2651
0
  return -1;
2652
0
}
2653
2654
/* Send message of a SPOE group. This is the action_ptr callback of a rule
2655
 * associated to a "send-spoe-group" action.
2656
 *
2657
 * It returns ACT_RET_CONT if processing is finished (with error or not), it returns
2658
 * ACT_RET_YIELD if the action is in progress. */
2659
static enum act_return spoe_send_group(struct act_rule *rule, struct proxy *px,
2660
               struct session *sess, struct stream *s, int flags)
2661
0
{
2662
0
  struct filter      *filter;
2663
0
  struct spoe_agent   *agent = NULL;
2664
0
  struct spoe_group   *group = NULL;
2665
0
  struct spoe_context *ctx   = NULL;
2666
0
  int ret, dir;
2667
2668
0
  list_for_each_entry(filter, &s->strm_flt.filters, list) {
2669
0
    if (filter->config == rule->arg.act.p[0]) {
2670
0
      agent = rule->arg.act.p[2];
2671
0
      group = rule->arg.act.p[3];
2672
0
      ctx   = filter->ctx;
2673
0
      break;
2674
0
    }
2675
0
  }
2676
0
  if (agent == NULL || group == NULL || ctx == NULL)
2677
0
    return ACT_RET_CONT;
2678
0
  if (ctx->state == SPOE_CTX_ST_NONE)
2679
0
    return ACT_RET_CONT;
2680
2681
0
  switch (rule->from) {
2682
0
    case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
2683
0
    case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
2684
0
    case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
2685
0
    case ACT_F_HTTP_REQ:    dir = SMP_OPT_DIR_REQ; break;
2686
0
    case ACT_F_HTTP_RES:    dir = SMP_OPT_DIR_RES; break;
2687
0
    default:
2688
0
      send_log(px, LOG_ERR, "SPOE: [%s] internal error while execute spoe-send-group\n",
2689
0
         agent->id);
2690
0
      return ACT_RET_CONT;
2691
0
  }
2692
2693
0
  ret = spoe_process_group(s, ctx, group, dir);
2694
0
  if (ret == 1)
2695
0
    return ACT_RET_CONT;
2696
0
  else if (ret == 0) {
2697
0
    if (flags & ACT_OPT_FINAL) {
2698
0
      ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
2699
0
      spoe_stop_processing(agent, ctx);
2700
0
      spoe_handle_processing_error(s, agent, ctx, dir);
2701
0
      return ACT_RET_CONT;
2702
0
    }
2703
0
    return ACT_RET_YIELD;
2704
0
  }
2705
0
  else
2706
0
    return ACT_RET_CONT;
2707
0
}
2708
2709
/* Check an "send-spoe-group" action. Here, we'll try to find the real SPOE
2710
 * group associated to <rule>. The format of an rule using 'send-spoe-group'
2711
 * action should be:
2712
 *
2713
 *   (http|tcp)-(request|response) send-spoe-group <engine-id> <group-id>
2714
 *
2715
 * So, we'll loop on each configured SPOE filter for the proxy <px> to find the
2716
 * SPOE engine matching <engine-id>. And then, we'll try to find the good group
2717
 * matching <group-id>. Finally, we'll check all messages referenced by the SPOE
2718
 * group.
2719
 *
2720
 * The function returns 1 in success case, otherwise, it returns 0 and err is
2721
 * filled.
2722
 */
2723
static int check_send_spoe_group(struct act_rule *rule, struct proxy *px, char **err)
2724
0
{
2725
0
  struct flt_conf     *fconf;
2726
0
  struct spoe_config  *conf;
2727
0
  struct spoe_agent   *agent = NULL;
2728
0
  struct spoe_group   *group;
2729
0
  struct spoe_message *msg;
2730
0
  char                *engine_id = rule->arg.act.p[0];
2731
0
  char                *group_id  = rule->arg.act.p[1];
2732
0
  unsigned int         where = 0;
2733
2734
0
  switch (rule->from) {
2735
0
    case ACT_F_TCP_REQ_SES: where = SMP_VAL_FE_SES_ACC; break;
2736
0
    case ACT_F_TCP_REQ_CNT: where = SMP_VAL_FE_REQ_CNT; break;
2737
0
    case ACT_F_TCP_RES_CNT: where = SMP_VAL_BE_RES_CNT; break;
2738
0
    case ACT_F_HTTP_REQ:    where = SMP_VAL_FE_HRQ_HDR; break;
2739
0
    case ACT_F_HTTP_RES:    where = SMP_VAL_BE_HRS_HDR; break;
2740
0
    default:
2741
0
      memprintf(err,
2742
0
          "internal error, unexpected rule->from=%d, please report this bug!",
2743
0
          rule->from);
2744
0
      goto error;
2745
0
  }
2746
2747
  /* Try to find the SPOE engine by checking all SPOE filters for proxy
2748
   * <px> */
2749
0
  list_for_each_entry(fconf, &px->filter_configs, list) {
2750
0
    conf = fconf->conf;
2751
2752
    /* This is not an SPOE filter */
2753
0
    if (fconf->id != spoe_filter_id)
2754
0
      continue;
2755
2756
    /* This is the good engine */
2757
0
    if (strcmp(conf->id, engine_id) == 0) {
2758
0
      agent = conf->agent;
2759
0
      break;
2760
0
    }
2761
0
  }
2762
0
  if (agent == NULL) {
2763
0
    memprintf(err, "unable to find SPOE engine '%s' used by the send-spoe-group '%s'",
2764
0
        engine_id, group_id);
2765
0
    goto error;
2766
0
  }
2767
2768
  /* Try to find the right group */
2769
0
  list_for_each_entry(group, &agent->groups, list) {
2770
    /* This is the good group */
2771
0
    if (strcmp(group->id, group_id) == 0)
2772
0
      break;
2773
0
  }
2774
0
  if (&group->list == &agent->groups) {
2775
0
    memprintf(err, "unable to find SPOE group '%s' into SPOE engine '%s' configuration",
2776
0
        group_id, engine_id);
2777
0
    goto error;
2778
0
  }
2779
2780
  /* Ok, we found the group, we need to check messages and their
2781
   * arguments */
2782
0
  list_for_each_entry(msg, &group->messages, by_grp) {
2783
0
    struct spoe_arg *arg;
2784
2785
0
    list_for_each_entry(arg, &msg->args, list) {
2786
0
      if (!(arg->expr->fetch->val & where)) {
2787
0
        memprintf(err, "Invalid SPOE message '%s' used by SPOE group '%s' at %s:%d: "
2788
0
            "some args extract information from '%s',"
2789
0
            "none of which is available here ('%s')",
2790
0
            msg->id, group->id, msg->conf.file, msg->conf.line,
2791
0
            sample_ckp_names(arg->expr->fetch->use),
2792
0
            sample_ckp_names(where));
2793
0
        goto error;
2794
0
      }
2795
0
    }
2796
0
  }
2797
2798
0
  free(engine_id);
2799
0
  free(group_id);
2800
0
  rule->arg.act.p[0] = fconf; /* Associate filter config with the rule */
2801
0
  rule->arg.act.p[1] = conf;  /* Associate SPOE config with the rule */
2802
0
  rule->arg.act.p[2] = agent; /* Associate SPOE agent with the rule */
2803
0
  rule->arg.act.p[3] = group; /* Associate SPOE group with the rule */
2804
0
  return 1;
2805
2806
0
  error:
2807
0
  free(engine_id);
2808
0
  free(group_id);
2809
0
  return 0;
2810
0
}
2811
2812
/* Parse 'send-spoe-group' action following the format:
2813
 *
2814
 *     ... send-spoe-group <engine-id> <group-id>
2815
 *
2816
 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
2817
 * message. Otherwise, it returns ACT_RET_PRS_OK and parsing engine and group
2818
 * ids are saved and used later, when the rule will be checked.
2819
 */
2820
static enum act_parse_ret parse_send_spoe_group(const char **args, int *orig_arg, struct proxy *px,
2821
            struct act_rule *rule, char **err)
2822
0
{
2823
0
  if (!*args[*orig_arg] || !*args[*orig_arg+1] ||
2824
0
      (*args[*orig_arg+2] && strcmp(args[*orig_arg+2], "if") != 0 && strcmp(args[*orig_arg+2], "unless") != 0)) {
2825
0
    memprintf(err, "expects 2 arguments: <engine-id> <group-id>");
2826
0
    return ACT_RET_PRS_ERR;
2827
0
  }
2828
0
  rule->arg.act.p[0] = strdup(args[*orig_arg]);   /* Copy the SPOE engine id */
2829
0
  rule->arg.act.p[1] = strdup(args[*orig_arg+1]); /* Cope the SPOE group id */
2830
2831
0
  (*orig_arg) += 2;
2832
2833
0
  rule->action     = ACT_CUSTOM;
2834
0
  rule->action_ptr = spoe_send_group;
2835
0
  rule->check_ptr  = check_send_spoe_group;
2836
0
  return ACT_RET_PRS_OK;
2837
0
}
2838
2839
2840
/* returns the engine ID of the SPOE */
2841
static int smp_fetch_spoe_engine_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
2842
0
{
2843
0
  struct appctx *appctx;
2844
0
  struct spoe_agent *agent;
2845
2846
0
        if (!smp->strm)
2847
0
                return 0;
2848
2849
0
  appctx = sc_appctx(smp->strm->scf);
2850
0
  if (!appctx || appctx->applet != &spoe_applet)
2851
0
    return 0;
2852
2853
0
  agent = spoe_appctx_agent(appctx);
2854
0
  if (!agent)
2855
0
    return 0;
2856
2857
0
  smp->data.type = SMP_T_STR;
2858
0
  smp->data.u.str.area = agent->engine_id;
2859
0
  smp->data.u.str.data = strlen(agent->engine_id);
2860
0
  smp->flags = SMP_F_CONST;
2861
2862
0
  return 1;
2863
0
}
2864
2865
static int spoe_postcheck_spop_proxy(struct proxy *px)
2866
0
{
2867
0
  struct server *srv;
2868
0
  int err_code = ERR_NONE;
2869
2870
0
  if (!(px->cap & PR_CAP_BE) || px->mode != PR_MODE_SPOP)
2871
0
    goto out;
2872
2873
0
  for (srv = px->srv; srv; srv = srv->next) {
2874
0
    if (srv->pool_conn_name) {
2875
0
      ha_free(&srv->pool_conn_name);
2876
0
      release_sample_expr(srv->pool_conn_name_expr);
2877
0
    }
2878
0
    srv->pool_conn_name = strdup("spoe.engine-id");
2879
0
    if (!srv->pool_conn_name) {
2880
0
      err_code |= ERR_ALERT | ERR_FATAL;
2881
0
      goto out;
2882
0
    }
2883
0
    srv->pool_conn_name_expr = _parse_srv_expr(srv->pool_conn_name, &px->conf.args, NULL, 0, NULL);
2884
0
    if (!srv->pool_conn_name_expr) {
2885
0
      err_code |= ERR_ALERT | ERR_FATAL;
2886
0
      goto out;
2887
0
    }
2888
0
  }
2889
2890
0
  out:
2891
0
  return err_code;
2892
0
}
2893
2894
REGISTER_POST_PROXY_CHECK(spoe_postcheck_spop_proxy);
2895
2896
/* Declare the filter parser for "spoe" keyword */
2897
static struct flt_kw_list flt_kws = { "SPOE", { }, {
2898
    { "spoe", parse_spoe_flt, NULL },
2899
    { NULL, NULL, NULL },
2900
  }
2901
};
2902
2903
INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
2904
2905
/* Delcate the action parser for "spoe-action" keyword */
2906
static struct action_kw_list tcp_req_action_kws = { { }, {
2907
    { "send-spoe-group", parse_send_spoe_group },
2908
    { /* END */ },
2909
  }
2910
};
2911
2912
INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_action_kws);
2913
2914
static struct action_kw_list tcp_res_action_kws = { { }, {
2915
    { "send-spoe-group", parse_send_spoe_group },
2916
    { /* END */ },
2917
  }
2918
};
2919
2920
INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_action_kws);
2921
2922
static struct action_kw_list http_req_action_kws = { { }, {
2923
    { "send-spoe-group", parse_send_spoe_group },
2924
    { /* END */ },
2925
  }
2926
};
2927
2928
INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_action_kws);
2929
2930
static struct action_kw_list http_res_action_kws = { { }, {
2931
    { "send-spoe-group", parse_send_spoe_group },
2932
    { /* END */ },
2933
  }
2934
};
2935
2936
INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_action_kws);
2937
2938
2939
static struct sample_fetch_kw_list smp_kws = {ILH, {
2940
  { "spoe.engine-id",  smp_fetch_spoe_engine_id, 0, NULL, SMP_T_STR, SMP_USE_INTRN},
2941
  {},
2942
}};
2943
2944
INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);