CONTRIB: move spoa_example out of the tree
As previously mentioned SPOA code has nothing to do in the haproxy core since they're not dependent on haproxy's version. This one was moved to its own repository here with complete history: https://github.com/haproxy/spoa-example
This commit is contained in:
parent
2b71810cb3
commit
8695199aa8
|
@ -43,7 +43,6 @@
|
|||
/admin/iprange/ip6range
|
||||
/admin/iprange/iprange
|
||||
/admin/systemd/haproxy.service
|
||||
/contrib/spoa_example/spoa
|
||||
dev/base64/base64rev-gen
|
||||
dev/flags/flags
|
||||
dev/poll/poll
|
||||
|
|
|
@ -131,8 +131,7 @@ Files: addons/wurfl, doc/WURFL-device-detection.txt
|
|||
|
||||
SPOE
|
||||
Maintainer: Christopher Faulet <cfaulet@haproxy.com>
|
||||
Files: src/flt_spoe.c, include/haproxy/spoe*.h
|
||||
Files: contrib/spoa_example, doc/SPOE.txt
|
||||
Files: src/flt_spoe.c, include/haproxy/spoe*.h, doc/SPOE.txt
|
||||
|
||||
SSL
|
||||
Maintainer: Emeric Brun <ebrun@haproxy.com>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
CC = gcc
|
||||
LD = $(CC)
|
||||
|
||||
CFLAGS = -g -O2 -Wall -Werror -pthread
|
||||
INCS += -I./include
|
||||
LIBS = -lpthread -levent -levent_pthreads
|
||||
|
||||
OBJS = spoa.o
|
||||
|
||||
|
||||
spoa: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
install: spoa
|
||||
install spoa $(DESTDIR)$(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -f spoa $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(INCS) -c -o $@ $<
|
|
@ -1,88 +0,0 @@
|
|||
A Random IP reputation service acting as a Stream Processing Offload Agent
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
This is a very simple service that implement a "random" ip reputation
|
||||
service. It will return random scores for all checked IP addresses. It only
|
||||
shows you how to implement a ip reputation service or such kind of services
|
||||
using the SPOE.
|
||||
|
||||
|
||||
Start the service
|
||||
---------------------
|
||||
|
||||
After you have compiled it, to start the service, you just need to use "spoa"
|
||||
binary:
|
||||
|
||||
$> ./spoa -h
|
||||
Usage: ./spoa [-h] [-d] [-p <port>] [-n <num-workers>]
|
||||
-h Print this message
|
||||
-d Enable the debug mode
|
||||
-p <port> Specify the port to listen on (default: 12345)
|
||||
-n <num-workers> Specify the number of workers (default: 5)
|
||||
|
||||
Note: A worker is a thread.
|
||||
|
||||
|
||||
Configure a SPOE to use the service
|
||||
---------------------------------------
|
||||
|
||||
All information about SPOE configuration can be found in "doc/SPOE.txt". Here is
|
||||
the configuration template to use for your SPOE:
|
||||
|
||||
[ip-reputation]
|
||||
|
||||
spoe-agent iprep-agent
|
||||
messages check-client-ip
|
||||
|
||||
option var-prefix iprep
|
||||
|
||||
timeout hello 100ms
|
||||
timeout idle 30s
|
||||
timeout processing 15ms
|
||||
|
||||
use-backend iprep-backend
|
||||
|
||||
spoe-message check-client-ip
|
||||
args src
|
||||
event on-client-session
|
||||
|
||||
|
||||
The engine is in the scope "ip-reputation". So to enable it, you must set the
|
||||
following line in a frontend/listener section:
|
||||
|
||||
frontend my-front
|
||||
...
|
||||
filter spoe engine ip-reputation config /path/spoe-ip-reputation.conf
|
||||
....
|
||||
|
||||
where "/path/spoe-ip-reputation.conf" is the path to your SPOE configuration
|
||||
file. The engine name is important here, it must be the same than the one used
|
||||
in the SPOE configuration file.
|
||||
|
||||
IMPORTANT NOTE:
|
||||
Because we want to send a message on the "on-client-session" event, this
|
||||
SPOE must be attached to a proxy with the frontend capability. If it is
|
||||
declared in a backend section, it will have no effet.
|
||||
|
||||
|
||||
Because, in SPOE configuration file, we declare to use the backend
|
||||
"iprep-backend" to communicate with the service, you must define it in HAProxy
|
||||
configuration. For example:
|
||||
|
||||
backend iprep-backend
|
||||
mode tcp
|
||||
timeout server 1m
|
||||
server iprep-srv 127.0.0.1:12345 check maxconn 5
|
||||
|
||||
|
||||
In reply to the "check-client-ip" message, this service will set the variable
|
||||
"ip_score" for the session, an integer between 0 and 100. If unchanged, the
|
||||
variable prefix is "iprep". So the full variable name will be
|
||||
"sess.iprep.ip_score".
|
||||
|
||||
You can use it in ACLs to experiment the SPOE feature. For example:
|
||||
|
||||
tcp-request content reject if { var(sess.iprep.ip_score) -m int lt 20 }
|
||||
|
||||
With this rule, all IP address with a score lower than 20 will be rejected
|
||||
(Remember, this score is random).
|
|
@ -1,95 +0,0 @@
|
|||
#ifndef _COMMON_MINI_CLIST_H
|
||||
#define _COMMON_MINI_CLIST_H
|
||||
|
||||
/* these are circular or bidirectionnal lists only. Each list pointer points to
|
||||
* another list pointer in a structure, and not the structure itself. The
|
||||
* pointer to the next element MUST be the first one so that the list is easily
|
||||
* cast as a single linked list or pointer.
|
||||
*/
|
||||
struct list {
|
||||
struct list *n; /* next */
|
||||
struct list *p; /* prev */
|
||||
};
|
||||
|
||||
/* First undefine some macros which happen to also be defined on OpenBSD,
|
||||
* in sys/queue.h, used by sys/event.h
|
||||
*/
|
||||
#undef LIST_HEAD
|
||||
#undef LIST_INIT
|
||||
#undef LIST_NEXT
|
||||
|
||||
/* ILH = Initialized List Head : used to prevent gcc from moving an empty
|
||||
* list to BSS. Some older version tend to trim all the array and cause
|
||||
* corruption.
|
||||
*/
|
||||
#define ILH { .n = (struct list *)1, .p = (struct list *)2 }
|
||||
|
||||
#define LIST_HEAD(a) ((void *)(&(a)))
|
||||
|
||||
#define LIST_INIT(l) ((l)->n = (l)->p = (l))
|
||||
|
||||
#define LIST_HEAD_INIT(l) { &l, &l }
|
||||
|
||||
/* adds an element at the beginning of a list ; returns the element */
|
||||
#define LIST_INSERT(lh, el) ({ (el)->n = (lh)->n; (el)->n->p = (lh)->n = (el); (el)->p = (lh); (el); })
|
||||
|
||||
/* adds an element at the end of a list ; returns the element */
|
||||
#define LIST_APPEND(lh, el) ({ (el)->p = (lh)->p; (el)->p->n = (lh)->p = (el); (el)->n = (lh); (el); })
|
||||
|
||||
/* removes an element from a list and returns it */
|
||||
#define LIST_DELETE(el) ({ typeof(el) __ret = (el); (el)->n->p = (el)->p; (el)->p->n = (el)->n; (__ret); })
|
||||
|
||||
/* returns a pointer of type <pt> to a structure containing a list head called
|
||||
* <el> at address <lh>. Note that <lh> can be the result of a function or macro
|
||||
* since it's used only once.
|
||||
* Example: LIST_ELEM(cur_node->args.next, struct node *, args)
|
||||
*/
|
||||
#define LIST_ELEM(lh, pt, el) ((pt)(((const char *)(lh)) - ((size_t)&((pt)NULL)->el)))
|
||||
|
||||
/* checks if the list head <lh> is empty or not */
|
||||
#define LIST_ISEMPTY(lh) ((lh)->n == (lh))
|
||||
|
||||
/* returns a pointer of type <pt> to a structure following the element
|
||||
* which contains list head <lh>, which is known as element <el> in
|
||||
* struct pt.
|
||||
* Example: LIST_NEXT(args, struct node *, list)
|
||||
*/
|
||||
#define LIST_NEXT(lh, pt, el) (LIST_ELEM((lh)->n, pt, el))
|
||||
|
||||
|
||||
/* returns a pointer of type <pt> to a structure preceding the element
|
||||
* which contains list head <lh>, which is known as element <el> in
|
||||
* struct pt.
|
||||
*/
|
||||
#undef LIST_PREV
|
||||
#define LIST_PREV(lh, pt, el) (LIST_ELEM((lh)->p, pt, el))
|
||||
|
||||
/*
|
||||
* Simpler FOREACH_ITEM macro inspired from Linux sources.
|
||||
* Iterates <item> through a list of items of type "typeof(*item)" which are
|
||||
* linked via a "struct list" member named <member>. A pointer to the head of
|
||||
* the list is passed in <list_head>. No temporary variable is needed. Note
|
||||
* that <item> must not be modified during the loop.
|
||||
* Example: list_for_each_entry(cur_acl, known_acl, list) { ... };
|
||||
*/
|
||||
#define list_for_each_entry(item, list_head, member) \
|
||||
for (item = LIST_ELEM((list_head)->n, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = LIST_ELEM(item->member.n, typeof(item), member))
|
||||
|
||||
/*
|
||||
* Simpler FOREACH_ITEM_SAFE macro inspired from Linux sources.
|
||||
* Iterates <item> through a list of items of type "typeof(*item)" which are
|
||||
* linked via a "struct list" member named <member>. A pointer to the head of
|
||||
* the list is passed in <list_head>. A temporary variable <back> of same type
|
||||
* as <item> is needed so that <item> may safely be deleted if needed.
|
||||
* Example: list_for_each_entry_safe(cur_acl, tmp, known_acl, list) { ... };
|
||||
*/
|
||||
#define list_for_each_entry_safe(item, back, list_head, member) \
|
||||
for (item = LIST_ELEM((list_head)->n, typeof(item), member), \
|
||||
back = LIST_ELEM(item->member.n, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = back, back = LIST_ELEM(back->member.n, typeof(back), member))
|
||||
|
||||
|
||||
#endif /* _COMMON_MINI_CLIST_H */
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* include/spoe_types.h
|
||||
* Macros, variables and structures for the SPOE filter.
|
||||
*
|
||||
* Copyright (C) 2017 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, version 2.1
|
||||
* exclusively.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _SPOE_TYPES_H
|
||||
#define _SPOE_TYPES_H
|
||||
|
||||
#include <mini-clist.h>
|
||||
|
||||
// Taken from HAProxy's defaults.h
|
||||
/* Maximum host name length */
|
||||
#ifndef MAX_HOSTNAME_LEN
|
||||
#if MAXHOSTNAMELEN
|
||||
#define MAX_HOSTNAME_LEN MAXHOSTNAMELEN
|
||||
#else
|
||||
#define MAX_HOSTNAME_LEN 64
|
||||
#endif // MAXHOSTNAMELEN
|
||||
#endif // MAX_HOSTNAME_LEN
|
||||
|
||||
/* Flags set on the SPOE agent */
|
||||
#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */
|
||||
#define SPOE_FL_PIPELINING 0x00000002 /* Set when SPOE agent supports pipelining (set by default) */
|
||||
#define SPOE_FL_ASYNC 0x00000004 /* Set when SPOE agent supports async (set by default) */
|
||||
#define SPOE_FL_SND_FRAGMENTATION 0x00000008 /* Set when SPOE agent supports sending fragmented payload */
|
||||
#define SPOE_FL_RCV_FRAGMENTATION 0x00000010 /* Set when SPOE agent supports receiving fragmented payload */
|
||||
|
||||
/* Flags set on the SPOE context */
|
||||
#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
|
||||
#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
|
||||
#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */
|
||||
#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */
|
||||
#define SPOE_CTX_FL_FRAGMENTED 0x00000010 /* Set when a fragmented frame is processing */
|
||||
|
||||
#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
|
||||
|
||||
/* Flags set on the SPOE applet */
|
||||
#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */
|
||||
#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */
|
||||
#define SPOE_APPCTX_FL_FRAGMENTATION 0x00000004 /* Set if fragmentation is supported */
|
||||
#define SPOE_APPCTX_FL_PERSIST 0x00000008 /* Set if the applet is persistent */
|
||||
|
||||
#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */
|
||||
#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */
|
||||
|
||||
/* Flags set on the SPOE frame */
|
||||
#define SPOE_FRM_FL_FIN 0x00000001
|
||||
#define SPOE_FRM_FL_ABRT 0x00000002
|
||||
|
||||
/* All supported SPOE actions */
|
||||
enum spoe_action_type {
|
||||
SPOE_ACT_T_SET_VAR = 1,
|
||||
SPOE_ACT_T_UNSET_VAR,
|
||||
SPOE_ACT_TYPES,
|
||||
};
|
||||
|
||||
/* All supported SPOE events */
|
||||
enum spoe_event {
|
||||
SPOE_EV_NONE = 0,
|
||||
|
||||
/* Request events */
|
||||
SPOE_EV_ON_CLIENT_SESS = 1,
|
||||
SPOE_EV_ON_TCP_REQ_FE,
|
||||
SPOE_EV_ON_TCP_REQ_BE,
|
||||
SPOE_EV_ON_HTTP_REQ_FE,
|
||||
SPOE_EV_ON_HTTP_REQ_BE,
|
||||
|
||||
/* Response events */
|
||||
SPOE_EV_ON_SERVER_SESS,
|
||||
SPOE_EV_ON_TCP_RSP,
|
||||
SPOE_EV_ON_HTTP_RSP,
|
||||
|
||||
SPOE_EV_EVENTS
|
||||
};
|
||||
|
||||
/* Errors triggered by streams */
|
||||
enum spoe_context_error {
|
||||
SPOE_CTX_ERR_NONE = 0,
|
||||
SPOE_CTX_ERR_TOUT,
|
||||
SPOE_CTX_ERR_RES,
|
||||
SPOE_CTX_ERR_TOO_BIG,
|
||||
SPOE_CTX_ERR_FRAG_FRAME_ABRT,
|
||||
SPOE_CTX_ERR_UNKNOWN = 255,
|
||||
SPOE_CTX_ERRS,
|
||||
};
|
||||
|
||||
/* Errors triggered by SPOE applet */
|
||||
enum spoe_frame_error {
|
||||
SPOE_FRM_ERR_NONE = 0,
|
||||
SPOE_FRM_ERR_IO,
|
||||
SPOE_FRM_ERR_TOUT,
|
||||
SPOE_FRM_ERR_TOO_BIG,
|
||||
SPOE_FRM_ERR_INVALID,
|
||||
SPOE_FRM_ERR_NO_VSN,
|
||||
SPOE_FRM_ERR_NO_FRAME_SIZE,
|
||||
SPOE_FRM_ERR_NO_CAP,
|
||||
SPOE_FRM_ERR_BAD_VSN,
|
||||
SPOE_FRM_ERR_BAD_FRAME_SIZE,
|
||||
SPOE_FRM_ERR_FRAG_NOT_SUPPORTED,
|
||||
SPOE_FRM_ERR_INTERLACED_FRAMES,
|
||||
SPOE_FRM_ERR_FRAMEID_NOTFOUND,
|
||||
SPOE_FRM_ERR_RES,
|
||||
SPOE_FRM_ERR_UNKNOWN = 99,
|
||||
SPOE_FRM_ERRS,
|
||||
};
|
||||
|
||||
/* Scopes used for variables set by agents. It is a way to be agnotic to vars
|
||||
* scope. */
|
||||
enum spoe_vars_scope {
|
||||
SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
|
||||
SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
|
||||
SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
|
||||
SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
|
||||
SPOE_SCOPE_RES, /* <=> SCOPE_RES */
|
||||
};
|
||||
|
||||
|
||||
/* Describe an argument that will be linked to a message. It is a sample fetch,
|
||||
* with an optional name. */
|
||||
struct spoe_arg {
|
||||
char *name; /* Name of the argument, may be NULL */
|
||||
unsigned int name_len; /* The name length, 0 if NULL */
|
||||
struct sample_expr *expr; /* Sample expression */
|
||||
struct list list; /* Used to chain SPOE args */
|
||||
};
|
||||
|
||||
/* Used during the config parsing only because, when a SPOE agent section is
|
||||
* parsed, messages can be undefined. */
|
||||
struct spoe_msg_placeholder {
|
||||
char *id; /* SPOE message placeholder id */
|
||||
struct list list; /* Use to chain SPOE message placeholders */
|
||||
};
|
||||
|
||||
/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
|
||||
* an argument list (see above) and it is linked to a specific event. */
|
||||
struct spoe_message {
|
||||
char *id; /* SPOE message id */
|
||||
unsigned int id_len; /* The message id length */
|
||||
struct spoe_agent *agent; /* SPOE agent owning this SPOE message */
|
||||
struct {
|
||||
char *file; /* file where the SPOE message appears */
|
||||
int line; /* line where the SPOE message appears */
|
||||
} conf; /* config information */
|
||||
unsigned int nargs; /* # of arguments */
|
||||
struct list args; /* Arguments added when the SPOE messages is sent */
|
||||
struct list list; /* Used to chain SPOE messages */
|
||||
|
||||
enum spoe_event event; /* SPOE_EV_* */
|
||||
};
|
||||
|
||||
enum spoe_frame_type {
|
||||
SPOE_FRM_T_UNSET = 0,
|
||||
|
||||
/* Frames sent by HAProxy */
|
||||
SPOE_FRM_T_HAPROXY_HELLO = 1,
|
||||
SPOE_FRM_T_HAPROXY_DISCON,
|
||||
SPOE_FRM_T_HAPROXY_NOTIFY,
|
||||
|
||||
/* Frames sent by the agents */
|
||||
SPOE_FRM_T_AGENT_HELLO = 101,
|
||||
SPOE_FRM_T_AGENT_DISCON,
|
||||
SPOE_FRM_T_AGENT_ACK
|
||||
};
|
||||
|
||||
/* All supported data types */
|
||||
enum spoe_data_type {
|
||||
SPOE_DATA_T_NULL = 0,
|
||||
SPOE_DATA_T_BOOL,
|
||||
SPOE_DATA_T_INT32,
|
||||
SPOE_DATA_T_UINT32,
|
||||
SPOE_DATA_T_INT64,
|
||||
SPOE_DATA_T_UINT64,
|
||||
SPOE_DATA_T_IPV4,
|
||||
SPOE_DATA_T_IPV6,
|
||||
SPOE_DATA_T_STR,
|
||||
SPOE_DATA_T_BIN,
|
||||
SPOE_DATA_TYPES
|
||||
};
|
||||
|
||||
/* a memory block of arbitrary size, or a string */
|
||||
struct chunk {
|
||||
char *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* all data types that may be encoded/decoded for each spoe_data_type */
|
||||
union spoe_data {
|
||||
bool boolean;
|
||||
int32_t int32;
|
||||
uint32_t uint32;
|
||||
int64_t int64;
|
||||
uint64_t uint64;
|
||||
struct in_addr ipv4;
|
||||
struct in6_addr ipv6;
|
||||
struct chunk chk; /* types STR and BIN */
|
||||
};
|
||||
|
||||
/* Masks to get data type or flags value */
|
||||
#define SPOE_DATA_T_MASK 0x0F
|
||||
#define SPOE_DATA_FL_MASK 0xF0
|
||||
|
||||
/* Flags to set Boolean values */
|
||||
#define SPOE_DATA_FL_FALSE 0x00
|
||||
#define SPOE_DATA_FL_TRUE 0x10
|
||||
|
||||
|
||||
#endif /* _TYPES_SPOE_H */
|
|
@ -1,430 +0,0 @@
|
|||
#ifndef _SPOP_FUNCTIONS_H
|
||||
#define _SPOP_FUNCTIONS_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <spoe_types.h>
|
||||
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
/* Encode the integer <i> into a varint (variable-length integer). The encoded
|
||||
* value is copied in <*buf>. Here is the encoding format:
|
||||
*
|
||||
* 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ]
|
||||
* 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ]
|
||||
* 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ]
|
||||
* 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ]
|
||||
* 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ]
|
||||
* ...
|
||||
*
|
||||
* On success, it returns the number of written bytes and <*buf> is moved after
|
||||
* the encoded value. Otherwise, it returns -1. */
|
||||
static inline int
|
||||
encode_varint(uint64_t i, char **buf, char *end)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)*buf;
|
||||
int r;
|
||||
|
||||
if (p >= (unsigned char *)end)
|
||||
return -1;
|
||||
|
||||
if (i < 240) {
|
||||
*p++ = i;
|
||||
*buf = (char *)p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
*p++ = (unsigned char)i | 240;
|
||||
i = (i - 240) >> 4;
|
||||
while (i >= 128) {
|
||||
if (p >= (unsigned char *)end)
|
||||
return -1;
|
||||
*p++ = (unsigned char)i | 128;
|
||||
i = (i - 128) >> 7;
|
||||
}
|
||||
|
||||
if (p >= (unsigned char *)end)
|
||||
return -1;
|
||||
*p++ = (unsigned char)i;
|
||||
|
||||
r = ((char *)p - *buf);
|
||||
*buf = (char *)p;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Decode a varint from <*buf> and save the decoded value in <*i>. See
|
||||
* 'spoe_encode_varint' for details about varint.
|
||||
* On success, it returns the number of read bytes and <*buf> is moved after the
|
||||
* varint. Otherwise, it returns -1. */
|
||||
static inline int
|
||||
decode_varint(char **buf, char *end, uint64_t *i)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)*buf;
|
||||
int r;
|
||||
|
||||
if (p >= (unsigned char *)end)
|
||||
return -1;
|
||||
|
||||
*i = *p++;
|
||||
if (*i < 240) {
|
||||
*buf = (char *)p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = 4;
|
||||
do {
|
||||
if (p >= (unsigned char *)end)
|
||||
return -1;
|
||||
*i += (uint64_t)*p << r;
|
||||
r += 7;
|
||||
} while (*p++ >= 128);
|
||||
|
||||
r = ((char *)p - *buf);
|
||||
*buf = (char *)p;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy
|
||||
* of <str>. It must have enough space in <*buf> to encode the buffer, else an
|
||||
* error is triggered.
|
||||
* On success, it returns <len> and <*buf> is moved after the encoded value. If
|
||||
* an error occurred, it returns -1. */
|
||||
static inline int
|
||||
spoe_encode_buffer(const char *str, size_t len, char **buf, char *end)
|
||||
{
|
||||
char *p = *buf;
|
||||
int ret;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
if (!len) {
|
||||
*p++ = 0;
|
||||
*buf = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = encode_varint(len, &p, end);
|
||||
if (ret == -1 || p + len > end)
|
||||
return -1;
|
||||
|
||||
memcpy(p, str, len);
|
||||
*buf = p + len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Encode a buffer, possibly partially. It does the same thing than
|
||||
* 'spoe_encode_buffer', but if there is not enough space, it does not fail.
|
||||
* On success, it returns the number of copied bytes and <*buf> is moved after
|
||||
* the encoded value. If an error occurred, it returns -1. */
|
||||
static inline int
|
||||
spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end)
|
||||
{
|
||||
char *p = *buf;
|
||||
int ret;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
if (!len) {
|
||||
*p++ = 0;
|
||||
*buf = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = encode_varint(len, &p, end);
|
||||
if (ret == -1 || p >= end)
|
||||
return -1;
|
||||
|
||||
ret = (p+len < end) ? len : (end - p);
|
||||
memcpy(p, str, ret);
|
||||
*buf = p + ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str>
|
||||
* points on the first byte of the buffer.
|
||||
* On success, it returns the buffer length and <*buf> is moved after the
|
||||
* encoded buffer. Otherwise, it returns -1. */
|
||||
static inline int
|
||||
spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len)
|
||||
{
|
||||
char *p = *buf;
|
||||
uint64_t sz;
|
||||
int ret;
|
||||
|
||||
*str = NULL;
|
||||
*len = 0;
|
||||
|
||||
ret = decode_varint(&p, end, &sz);
|
||||
if (ret == -1 || p + sz > end)
|
||||
return -1;
|
||||
|
||||
*str = p;
|
||||
*len = sz;
|
||||
*buf = p + sz;
|
||||
return sz;
|
||||
}
|
||||
|
||||
/* Encode a typed data using value in <data> and type <type>. On success, it
|
||||
* returns the number of copied bytes and <*buf> is moved after the encoded
|
||||
* value. If an error occurred, it returns -1.
|
||||
*
|
||||
* If the value is too big to be encoded, depending on its type, then encoding
|
||||
* failed or the value is partially encoded. Only strings and binaries can be
|
||||
* partially encoded. In this case, the offset <*off> is updated to known how
|
||||
* many bytes has been encoded. If <*off> is zero at the end, it means that all
|
||||
* data has been encoded. */
|
||||
static inline int
|
||||
spoe_encode_data(union spoe_data *data, enum spoe_data_type type, unsigned int *off, char **buf, char *end)
|
||||
{
|
||||
char *p = *buf;
|
||||
int ret;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
if (data == NULL) {
|
||||
*p++ = SPOE_DATA_T_NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
*p++ = type;
|
||||
switch (type) {
|
||||
case SPOE_DATA_T_BOOL:
|
||||
p[-1] |= (data->boolean ? SPOE_DATA_FL_TRUE : SPOE_DATA_FL_FALSE);
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_INT32:
|
||||
if (encode_varint(data->int32, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_UINT32:
|
||||
if (encode_varint(data->uint32, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_INT64:
|
||||
if (encode_varint(data->int64, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_UINT64:
|
||||
if (encode_varint(data->uint64, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_IPV4:
|
||||
if (p + 4 > end)
|
||||
return -1;
|
||||
memcpy(p, &data->ipv4, 4);
|
||||
p += 4;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_IPV6:
|
||||
if (p + 16 > end)
|
||||
return -1;
|
||||
memcpy(p, &data->ipv6, 16);
|
||||
p += 16;
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_STR:
|
||||
case SPOE_DATA_T_BIN: {
|
||||
/* Here, we need to know if the sample has already been
|
||||
* partially encoded. If yes, we only need to encode the
|
||||
* remaining, <*off> reprensenting the number of bytes
|
||||
* already encoded. */
|
||||
if (!*off) {
|
||||
/* First evaluation of the sample : encode the
|
||||
* type (string or binary), the buffer length
|
||||
* (as a varint) and at least 1 byte of the
|
||||
* buffer. */
|
||||
ret = spoe_encode_frag_buffer(data->chk.ptr, data->chk.len, &p, end);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* The sample has been fragmented, encode remaining data */
|
||||
ret = MIN(data->chk.len - *off, end - p);
|
||||
memcpy(p, data->chk.ptr + *off, ret);
|
||||
p += ret;
|
||||
}
|
||||
/* Now update <*off> */
|
||||
if (ret + *off != data->chk.len)
|
||||
*off += ret;
|
||||
else
|
||||
*off = 0;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case SMP_T_METH: {
|
||||
char *m;
|
||||
size_t len;
|
||||
|
||||
*p++ = SPOE_DATA_T_STR;
|
||||
switch (smp->data.u.meth.meth) {
|
||||
case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break;
|
||||
case HTTP_METH_GET : m = "GET"; len = 3; break;
|
||||
case HTTP_METH_HEAD : m = "HEAD"; len = 4; break;
|
||||
case HTTP_METH_POST : m = "POST"; len = 4; break;
|
||||
case HTTP_METH_PUT : m = "PUT"; len = 3; break;
|
||||
case HTTP_METH_DELETE : m = "DELETE"; len = 6; break;
|
||||
case HTTP_METH_TRACE : m = "TRACE"; len = 5; break;
|
||||
case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break;
|
||||
|
||||
default :
|
||||
m = smp->data.u.meth.str.str;
|
||||
len = smp->data.u.meth.str.len;
|
||||
}
|
||||
if (spoe_encode_buffer(m, len, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
default:
|
||||
/* send type NULL for unknown types */
|
||||
p[-1] = SPOE_DATA_T_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
ret = (p - *buf);
|
||||
*buf = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
|
||||
* of skipped bytes is returned and the <*buf> is moved after skipped data.
|
||||
*
|
||||
* A types data is composed of a type (1 byte) and corresponding data:
|
||||
* - boolean: non additional data (0 bytes)
|
||||
* - integers: a variable-length integer (see decode_varint)
|
||||
* - ipv4: 4 bytes
|
||||
* - ipv6: 16 bytes
|
||||
* - binary and string: a buffer prefixed by its size, a variable-length
|
||||
* integer (see spoe_decode_buffer) */
|
||||
static inline int
|
||||
spoe_skip_data(char **buf, char *end)
|
||||
{
|
||||
char *str, *p = *buf;
|
||||
int type, ret;
|
||||
uint64_t v, sz;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
type = *p++;
|
||||
switch (type & SPOE_DATA_T_MASK) {
|
||||
case SPOE_DATA_T_BOOL:
|
||||
break;
|
||||
case SPOE_DATA_T_INT32:
|
||||
case SPOE_DATA_T_INT64:
|
||||
case SPOE_DATA_T_UINT32:
|
||||
case SPOE_DATA_T_UINT64:
|
||||
if (decode_varint(&p, end, &v) == -1)
|
||||
return -1;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV4:
|
||||
if (p+4 > end)
|
||||
return -1;
|
||||
p += 4;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV6:
|
||||
if (p+16 > end)
|
||||
return -1;
|
||||
p += 16;
|
||||
break;
|
||||
case SPOE_DATA_T_STR:
|
||||
case SPOE_DATA_T_BIN:
|
||||
/* All the buffer must be skipped */
|
||||
if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = (p - *buf);
|
||||
*buf = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decode a typed data and fill <smp>. If an error occurred, -1 is returned,
|
||||
* otherwise the number of read bytes is returned and <*buf> is moved after the
|
||||
* decoded data. See spoe_skip_data for details. */
|
||||
static inline int
|
||||
spoe_decode_data(char **buf, char *end, union spoe_data *data, enum spoe_data_type *type)
|
||||
{
|
||||
char *str, *p = *buf;
|
||||
int v, r = 0;
|
||||
uint64_t sz;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
v = *p++;
|
||||
*type = v & SPOE_DATA_T_MASK;
|
||||
|
||||
switch (*type) {
|
||||
case SPOE_DATA_T_BOOL:
|
||||
data->boolean = ((v & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
|
||||
break;
|
||||
case SPOE_DATA_T_INT32:
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
return -1;
|
||||
data->int32 = sz;
|
||||
break;
|
||||
case SPOE_DATA_T_INT64:
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
return -1;
|
||||
data->int64 = sz;
|
||||
break;
|
||||
case SPOE_DATA_T_UINT32:
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
return -1;
|
||||
data->uint32 = sz;
|
||||
break;
|
||||
case SPOE_DATA_T_UINT64:
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
return -1;
|
||||
data->uint64 = sz;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV4:
|
||||
if (p+4 > end)
|
||||
return -1;
|
||||
memcpy(&data->ipv4, p, 4);
|
||||
p += 4;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV6:
|
||||
if (p+16 > end)
|
||||
return -1;
|
||||
memcpy(&data->ipv6, p, 16);
|
||||
p += 16;
|
||||
break;
|
||||
case SPOE_DATA_T_STR:
|
||||
case SPOE_DATA_T_BIN:
|
||||
/* All the buffer must be decoded */
|
||||
if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
|
||||
return -1;
|
||||
data->chk.ptr = str;
|
||||
data->chk.len = sz;
|
||||
break;
|
||||
default: /* SPOE_DATA_T_NULL, unknown */
|
||||
break;
|
||||
}
|
||||
|
||||
r = (p - *buf);
|
||||
*buf = p;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -77,6 +77,10 @@ and "http-response" rules. And it only supports variables definition. But, in
|
|||
spite of these limited features, we can easily imagine to implement SSO
|
||||
solution, ip reputation or ip geolocation services.
|
||||
|
||||
Some example implementations in various languages are linked to from the
|
||||
HAProxy Wiki page dedicated to this mechanism:
|
||||
|
||||
https://github.com/haproxy/wiki/wiki/SPOE:-Stream-Processing-Offloading-Engine
|
||||
|
||||
2. SPOE configuration
|
||||
----------------------
|
||||
|
|
Loading…
Reference in New Issue