CONTRIB: move modsecurity 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-modsecurity
This commit is contained in:
parent
da72723189
commit
b77cd7f562
1
.gitignore
vendored
1
.gitignore
vendored
@ -50,6 +50,5 @@ dev/tcploop/tcploop
|
||||
dev/hpack/decode
|
||||
dev/hpack/gen-rht
|
||||
contrib/mod_defender/defender
|
||||
contrib/modsecurity/modsecurity
|
||||
/src/dlmalloc.c
|
||||
/tests/test_hashes
|
||||
|
@ -1,52 +0,0 @@
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
CC ?= gcc
|
||||
LD = $(CC)
|
||||
|
||||
ifeq ($(MODSEC_INC),)
|
||||
MODSEC_INC := modsecurity-2.9.1/INSTALL/include
|
||||
endif
|
||||
|
||||
ifeq ($(MODSEC_LIB),)
|
||||
MODSEC_LIB := modsecurity-2.9.1/INSTALL/lib
|
||||
endif
|
||||
|
||||
ifeq ($(APACHE2_INC),)
|
||||
APACHE2_INC := /usr/include/apache2
|
||||
endif
|
||||
|
||||
ifeq ($(APR_INC),)
|
||||
APR_INC := /usr/include/apr-1.0
|
||||
endif
|
||||
|
||||
ifeq ($(LIBXML_INC),)
|
||||
LIBXML_INC := /usr/include/libxml2
|
||||
endif
|
||||
|
||||
ifeq ($(EVENT_LIB),)
|
||||
EVENT_LIB := -levent
|
||||
endif
|
||||
|
||||
ifeq ($(EVENT_INC),)
|
||||
EVENT_INC := /usr/include
|
||||
endif
|
||||
|
||||
CFLAGS += -g -Wall -pthread
|
||||
INCS += -Iinclude -I$(MODSEC_INC) -I$(APACHE2_INC) -I$(APR_INC) -I$(LIBXML_INC) -I$(EVENT_INC)
|
||||
LIBS += -lpthread $(EVENT_LIB) -levent_pthreads -lcurl -lapr-1 -laprutil-1 -lxml2 -lpcre -lyajl
|
||||
|
||||
OBJS = spoa.o modsec_wrapper.o
|
||||
|
||||
modsecurity: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(MODSEC_LIB)/standalone.a $(LIBS)
|
||||
|
||||
install: modsecurity
|
||||
install modsecurity $(DESTDIR)$(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -f modsecurity $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(INCS) -c -o $@ $<
|
@ -1,132 +0,0 @@
|
||||
ModSecurity for HAProxy
|
||||
-----------------------
|
||||
|
||||
This is a third party daemon which speaks SPOE. It gives requests send by HAProxy
|
||||
to ModSecurity and returns the verdict.
|
||||
|
||||
Compilation
|
||||
---------------
|
||||
|
||||
You must compile ModSecurity in standalone mode. Below an example for
|
||||
ModSecurity-2.9.1. Note that ModSecurity depends the Apache APR. I assume that
|
||||
the Apache dependencies are installed on the system.
|
||||
|
||||
./configure \
|
||||
--prefix=$PWD/INSTALL \
|
||||
--disable-apache2-module \
|
||||
--enable-standalone-module \
|
||||
--enable-pcre-study \
|
||||
--without-lua \
|
||||
--enable-pcre-jit
|
||||
make
|
||||
make -C standalone install
|
||||
mkdir -p $PWD/INSTALL/include
|
||||
cp standalone/*.h $PWD/INSTALL/include
|
||||
cp apache2/*.h $PWD/INSTALL/include
|
||||
|
||||
Note that this compilation method works, but is a little bit rustic. I can't
|
||||
deal with Lua, I supposed that is a dependencies problem on my computer.
|
||||
|
||||
Start the service
|
||||
---------------------
|
||||
|
||||
After you have compiled it, to start the service, you just need to use "spoa"
|
||||
binary:
|
||||
|
||||
$> ./modsecurity -h
|
||||
Usage: ./spoa [-h] [-d] [-p <port>] [-n <num-workers>] [-f <config-file>]
|
||||
-h Print this message
|
||||
-d Enable the debug mode
|
||||
-f <config-file> Modsecurity configuration file
|
||||
-m <max-frame-size> Specify the maximum frame size (default : 16384)
|
||||
-p <port> Specify the port to listen on (default: 12345)
|
||||
-n <num-workers> Specify the number of workers (default: 5)
|
||||
-c <capability> Enable the support of the specified capability
|
||||
-t <time> Set a delay to process a message (default: 0)
|
||||
The value is specified in milliseconds by default,
|
||||
but can be in any other unit if the number is suffixed
|
||||
by a unit (us, ms, s)
|
||||
|
||||
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 with ModSecurity module:
|
||||
|
||||
[modsecurity]
|
||||
|
||||
spoe-agent modsecurity-agent
|
||||
messages check-request
|
||||
option var-prefix modsec
|
||||
timeout hello 100ms
|
||||
timeout idle 30s
|
||||
timeout processing 15ms
|
||||
use-backend spoe-modsecurity
|
||||
|
||||
spoe-message check-request
|
||||
args unique-id method path query req.ver req.hdrs_bin req.body_size req.body
|
||||
event on-frontend-http-request
|
||||
|
||||
The engine is in the scope "modsecurity". So to enable it, you must set the
|
||||
following line in a frontend/listener section:
|
||||
|
||||
frontend my-front
|
||||
...
|
||||
filter spoe engine modsecurity config spoe-modsecurity.conf
|
||||
...
|
||||
|
||||
|
||||
Because, in SPOE configuration file, we declare to use the backend
|
||||
"spoe-modsecurity" to communicate with the service, you must define it in
|
||||
HAProxy configuration. For example:
|
||||
|
||||
backend spoe-modsecurity
|
||||
mode tcp
|
||||
balance roundrobin
|
||||
timeout connect 5s
|
||||
timeout server 3m
|
||||
server modsec1 127.0.0.1:12345
|
||||
|
||||
The modsecurity action is returned in a variable called txn.modsec.code. It
|
||||
contains the HTTP returned code. If the variable contains 0, the request is
|
||||
clean.
|
||||
|
||||
http-request deny if { var(txn.modsec.code) -m int gt 0 }
|
||||
|
||||
With this rule, all the request not clean are rejected.
|
||||
|
||||
|
||||
Known bugs, limitations and TODO list
|
||||
-----------------------------------------
|
||||
|
||||
Modsecurity bugs:
|
||||
-----------------
|
||||
|
||||
* When the audit_log is used with the directive "SecAuditLogType Serial", in
|
||||
some systems, the APR mutex initialisation silently fails, this causes a
|
||||
segmentation fault. For my own usage, I have a patched version of modsec where
|
||||
I use another mutex than "APR_LOCK_DEFAULT" like "APR_LOCK_PROC_PTHREAD"
|
||||
|
||||
- rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp);
|
||||
+ rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_PROC_PTHREAD, mp);
|
||||
|
||||
* Configuration file loaded with wildcard (eg. Include rules/*.conf), are loaded
|
||||
in reverse alphabetical order. You can found a patch below. The ModSecurity
|
||||
team ignored this patch.
|
||||
|
||||
https://github.com/SpiderLabs/ModSecurity/issues/1285
|
||||
http://www.arpalert.org/0001-Fix-bug-when-load-files.patch
|
||||
|
||||
Or insert includes without wildcards.
|
||||
|
||||
Todo:
|
||||
-----
|
||||
|
||||
* Clarify the partial body analysis.
|
||||
* The response body is not yet analyzed.
|
||||
* ModSecurity can't modify the response body.
|
||||
* Implements real log management. Actually, the log are sent on stderr.
|
||||
* Implements daemon things (forks, write a pid, etc.).
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/api-t.h
|
||||
* This provides definitions for all common types or type modifiers used
|
||||
* everywhere in the code, and suitable for use in structure fields.
|
||||
*
|
||||
* Copyright (C) 2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HAPROXY_TYPES_H
|
||||
#define _HAPROXY_TYPES_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <haproxy/compat.h>
|
||||
#include <haproxy/compiler.h>
|
||||
#include <haproxy/list-t.h>
|
||||
|
||||
#endif /* _HAPROXY_TYPES_H */
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/api.h
|
||||
*
|
||||
* Include wrapper that assembles all includes required by every haproxy file.
|
||||
* Please do not add direct definitions into this file.
|
||||
*
|
||||
* Copyright (C) 2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HAPROXY_BASE_H
|
||||
#define _HAPROXY_BASE_H
|
||||
|
||||
#include <haproxy/api-t.h>
|
||||
|
||||
#endif
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/buf-t.h
|
||||
* Simple buffer handling - types definitions.
|
||||
*
|
||||
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HAPROXY_BUF_T_H
|
||||
#define _HAPROXY_BUF_T_H
|
||||
|
||||
#include <haproxy/api-t.h>
|
||||
|
||||
/* Structure defining a buffer's head */
|
||||
struct buffer {
|
||||
size_t size; /* buffer size in bytes */
|
||||
char *area; /* points to <size> bytes */
|
||||
size_t data; /* amount of data after head including wrapping */
|
||||
size_t head; /* start offset of remaining data relative to area */
|
||||
};
|
||||
|
||||
/* A buffer may be in 3 different states :
|
||||
* - unallocated : size == 0, area == 0 (b_is_null() is true)
|
||||
* - waiting : size == 0, area != 0 (b_is_null() is true)
|
||||
* - allocated : size > 0, area > 0 (b_is_null() is false)
|
||||
*/
|
||||
|
||||
/* initializers for certain buffer states. It is important that the NULL buffer
|
||||
* remains the one with all fields initialized to zero so that a calloc() or a
|
||||
* memset() on a struct automatically sets a NULL buffer.
|
||||
*/
|
||||
#define BUF_NULL ((struct buffer){ })
|
||||
#define BUF_WANTED ((struct buffer){ .area = (char *)1 })
|
||||
#define BUF_RING ((struct buffer){ .area = (char *)2 })
|
||||
|
||||
#endif /* _HAPROXY_BUF_T_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -1,294 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/compat.h
|
||||
* Operating system compatibility interface.
|
||||
*
|
||||
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_COMPAT_H
|
||||
#define _HAPROXY_COMPAT_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
/* This is needed on Linux for Netfilter includes */
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
|
||||
/* These are a few short names for commonly used types whose size and sometimes
|
||||
* signedness depends on the architecture. Be careful not to rely on a few
|
||||
* common but wrong assumptions:
|
||||
* - char is not always signed (ARM, AARCH64, PPC)
|
||||
* - long is not always large enough for a pointer (Windows)
|
||||
* These types are needed with the standard C API (string.h, printf, syscalls).
|
||||
*
|
||||
* When a fixed size is needed (protocol interoperability), better use the
|
||||
* standard types provided by stdint.h:
|
||||
* - size_t : unsigned int of default word size, large enough for any
|
||||
* object in memory
|
||||
* - ssize_t : signed int of default word size, used by some syscalls
|
||||
* - uintptr_t : an unsigned int large enough to store any pointer
|
||||
* - ptrdiff_t : a signed int large enough to hold a distance between 2 ptrs
|
||||
* - int<size>_t : a signed int of <size> bits (8,16,32,64 work everywhere)
|
||||
* - uint<size>_t : an unsigned int of <size> bits
|
||||
*/
|
||||
typedef signed char schar;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned long long ullong;
|
||||
typedef long long llong;
|
||||
|
||||
|
||||
/* set any optional field in a struct to this type to save ifdefs. Its address
|
||||
* will still be valid but it will not reserve any room nor require any
|
||||
* initialization.
|
||||
*/
|
||||
typedef struct { } empty_t;
|
||||
|
||||
// Redefine some limits that are not present everywhere
|
||||
#ifndef LLONG_MAX
|
||||
# define LLONG_MAX 9223372036854775807LL
|
||||
# define LLONG_MIN (-LLONG_MAX - 1LL)
|
||||
#endif
|
||||
|
||||
#ifndef ULLONG_MAX
|
||||
# define ULLONG_MAX (LLONG_MAX * 2ULL + 1)
|
||||
#endif
|
||||
|
||||
#ifndef LONGBITS
|
||||
#define LONGBITS ((unsigned int)sizeof(long) * 8)
|
||||
#endif
|
||||
|
||||
#ifndef BITS_PER_INT
|
||||
#define BITS_PER_INT (8*sizeof(int))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* this is for libc5 for example */
|
||||
#ifndef TCP_NODELAY
|
||||
#define TCP_NODELAY 1
|
||||
#endif
|
||||
|
||||
#ifndef SHUT_RD
|
||||
#define SHUT_RD 0
|
||||
#endif
|
||||
|
||||
#ifndef SHUT_WR
|
||||
#define SHUT_WR 1
|
||||
#endif
|
||||
|
||||
/* only Linux defines it */
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
/* AIX does not define MSG_DONTWAIT. We'll define it to zero, and test it
|
||||
* wherever appropriate.
|
||||
*/
|
||||
#ifndef MSG_DONTWAIT
|
||||
#define MSG_DONTWAIT 0
|
||||
#endif
|
||||
|
||||
/* Only Linux defines MSG_MORE */
|
||||
#ifndef MSG_MORE
|
||||
#define MSG_MORE 0
|
||||
#endif
|
||||
|
||||
/* On Linux 2.4 and above, MSG_TRUNC can be used on TCP sockets to drop any
|
||||
* pending data. Let's rely on NETFILTER to detect if this is supported.
|
||||
*/
|
||||
#ifdef USE_NETFILTER
|
||||
#define MSG_TRUNC_CLEARS_INPUT
|
||||
#endif
|
||||
|
||||
/* Maximum path length, OS-dependant */
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 128
|
||||
#endif
|
||||
|
||||
/* longest UNIX socket name */
|
||||
#ifndef UNIX_MAX_PATH
|
||||
#define UNIX_MAX_PATH 108
|
||||
#endif
|
||||
|
||||
/* On Linux, allows pipes to be resized */
|
||||
#ifndef F_SETPIPE_SZ
|
||||
#define F_SETPIPE_SZ (1024 + 7)
|
||||
#endif
|
||||
|
||||
/* On FreeBSD we don't have SI_TKILL but SI_LWP instead */
|
||||
#if !defined(SI_TKILL) && defined(SI_LWP)
|
||||
#define SI_TKILL SI_LWP
|
||||
#endif
|
||||
|
||||
/* systems without such defines do not know clockid_t or timer_t */
|
||||
#if !(_POSIX_TIMERS > 0)
|
||||
#undef clockid_t
|
||||
#define clockid_t empty_t
|
||||
#undef timer_t
|
||||
#define timer_t empty_t
|
||||
#endif
|
||||
|
||||
/* define a dummy value to designate "no timer". Use only 32 bits. */
|
||||
#ifndef TIMER_INVALID
|
||||
#define TIMER_INVALID ((timer_t)(unsigned long)(0xfffffffful))
|
||||
#endif
|
||||
|
||||
#if defined(USE_TPROXY) && defined(USE_NETFILTER)
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#endif
|
||||
|
||||
/* On Linux, IP_TRANSPARENT and/or IP_FREEBIND generally require a kernel patch */
|
||||
#if defined(USE_LINUX_TPROXY)
|
||||
#if !defined(IP_FREEBIND)
|
||||
#define IP_FREEBIND 15
|
||||
#endif /* !IP_FREEBIND */
|
||||
#if !defined(IP_TRANSPARENT)
|
||||
#define IP_TRANSPARENT 19
|
||||
#endif /* !IP_TRANSPARENT */
|
||||
#if !defined(IPV6_TRANSPARENT)
|
||||
#define IPV6_TRANSPARENT 75
|
||||
#endif /* !IPV6_TRANSPARENT */
|
||||
#endif /* USE_LINUX_TPROXY */
|
||||
|
||||
#if defined(IP_FREEBIND) \
|
||||
|| defined(IP_BINDANY) \
|
||||
|| defined(IPV6_BINDANY) \
|
||||
|| defined(SO_BINDANY) \
|
||||
|| defined(IP_TRANSPARENT) \
|
||||
|| defined(IPV6_TRANSPARENT)
|
||||
#define CONFIG_HAP_TRANSPARENT
|
||||
#endif
|
||||
|
||||
/* We'll try to enable SO_REUSEPORT on Linux 2.4 and 2.6 if not defined.
|
||||
* There are two families of values depending on the architecture. Those
|
||||
* are at least valid on Linux 2.4 and 2.6, reason why we'll rely on the
|
||||
* USE_NETFILTER define.
|
||||
*/
|
||||
#if !defined(SO_REUSEPORT) && defined(USE_NETFILTER)
|
||||
#if (SO_REUSEADDR == 2)
|
||||
#define SO_REUSEPORT 15
|
||||
#elif (SO_REUSEADDR == 0x0004)
|
||||
#define SO_REUSEPORT 0x0200
|
||||
#endif /* SO_REUSEADDR */
|
||||
#endif /* SO_REUSEPORT */
|
||||
|
||||
/* only Linux defines TCP_FASTOPEN */
|
||||
#ifdef USE_TFO
|
||||
#ifndef TCP_FASTOPEN
|
||||
#define TCP_FASTOPEN 23
|
||||
#endif
|
||||
|
||||
#ifndef TCP_FASTOPEN_CONNECT
|
||||
#define TCP_FASTOPEN_CONNECT 30
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* If IPv6 is supported, define IN6_IS_ADDR_V4MAPPED() if missing. */
|
||||
#if defined(IPV6_TCLASS) && !defined(IN6_IS_ADDR_V4MAPPED)
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) \
|
||||
((((const uint32_t *) (a))[0] == 0) \
|
||||
&& (((const uint32_t *) (a))[1] == 0) \
|
||||
&& (((const uint32_t *) (a))[2] == htonl (0xffff)))
|
||||
#endif
|
||||
|
||||
#if defined(__dietlibc__)
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
/* crypt_r() has been present in glibc since 2.2 and on FreeBSD since 12.0
|
||||
* (12000002). No other OS makes any mention of it for now. Feel free to add
|
||||
* valid known combinations below if needed to relax the crypt() lock when
|
||||
* using threads.
|
||||
*/
|
||||
#if (defined(__GNU_LIBRARY__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) \
|
||||
|| (defined(__FreeBSD__) && __FreeBSD_version >= 1200002)
|
||||
#define HA_HAVE_CRYPT_R
|
||||
#endif
|
||||
|
||||
/* some backtrace() implementations are broken or incomplete, in this case we
|
||||
* can replace them. We must not do it all the time as some are more accurate
|
||||
* than ours.
|
||||
*/
|
||||
#ifdef USE_BACKTRACE
|
||||
#if defined(__aarch64__)
|
||||
/* on aarch64 at least from gcc-4.7.4 to 7.4.1 we only get a single entry, which
|
||||
* is pointless. Ours works though it misses the faulty function itself,
|
||||
* probably due to an alternate stack for the signal handler which does not
|
||||
* create a new frame hence doesn't store the caller's return address.
|
||||
*/
|
||||
#elif defined(__clang__) && defined(__x86_64__)
|
||||
/* this is on FreeBSD, clang 4.0 to 8.0 produce don't go further than the
|
||||
* sighandler.
|
||||
*/
|
||||
#else
|
||||
#define HA_HAVE_WORKING_BACKTRACE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* malloc_trim() can be very convenient to reclaim unused memory especially
|
||||
* from huge pattern files. It's available (and really usable) in glibc 2.8 and
|
||||
* above.
|
||||
*/
|
||||
#if (defined(__GNU_LIBRARY__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 8))
|
||||
#include <malloc.h>
|
||||
#define HA_HAVE_MALLOC_TRIM
|
||||
#endif
|
||||
|
||||
/* glibc 2.26 includes a thread-local cache which makes it fast enough in threads */
|
||||
#if (defined(__GNU_LIBRARY__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 26))
|
||||
#include <malloc.h>
|
||||
#define HA_HAVE_FAST_MALLOC
|
||||
#endif
|
||||
|
||||
/* Max number of file descriptors we send in one sendmsg(). Linux seems to be
|
||||
* able to send 253 fds per sendmsg(), not sure about the other OSes.
|
||||
*/
|
||||
#define MAX_SEND_FD 253
|
||||
|
||||
/* Make the new complex name for the xxhash function easier to remember
|
||||
* and use.
|
||||
*/
|
||||
#ifndef XXH3
|
||||
#define XXH3(data, len, seed) XXH3_64bits_withSeed(data, len, seed)
|
||||
#endif
|
||||
|
||||
#endif /* _HAPROXY_COMPAT_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -1,298 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/compiler.h
|
||||
* This files contains some compiler-specific settings.
|
||||
*
|
||||
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_COMPILER_H
|
||||
#define _HAPROXY_COMPILER_H
|
||||
|
||||
#ifdef DEBUG_USE_ABORT
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Gcc before 3.0 needs [0] to declare a variable-size array
|
||||
*/
|
||||
#ifndef VAR_ARRAY
|
||||
#if defined(__GNUC__) && (__GNUC__ < 3)
|
||||
#define VAR_ARRAY 0
|
||||
#else
|
||||
#define VAR_ARRAY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(__GNUC__)
|
||||
/* Some versions of glibc irresponsibly redefine __attribute__() to empty for
|
||||
* non-gcc compilers, and as such, silently break all constructors with other
|
||||
* other compilers. Let's make sure such incompatibilities are detected if any,
|
||||
* or that the attribute is properly enforced.
|
||||
*/
|
||||
#undef __attribute__
|
||||
#define __attribute__(x) __attribute__(x)
|
||||
#endif
|
||||
|
||||
/* By default, gcc does not inline large chunks of code, but we want it to
|
||||
* respect our choices.
|
||||
*/
|
||||
#if !defined(forceinline)
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 3)
|
||||
#define forceinline inline
|
||||
#else
|
||||
#define forceinline inline __attribute__((always_inline))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* silence the "unused" warnings without having to place painful #ifdefs.
|
||||
* For use with variables or functions.
|
||||
*/
|
||||
#define __maybe_unused __attribute__((unused))
|
||||
|
||||
/* These macros are used to declare a section name for a variable.
|
||||
* WARNING: keep section names short, as MacOS limits them to 16 characters.
|
||||
* The _START and _STOP attributes have to be placed after the start and stop
|
||||
* weak symbol declarations, and are only used by MacOS.
|
||||
*/
|
||||
#if !defined(USE_OBSOLETE_LINKER)
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define HA_SECTION(s) __attribute__((__section__("__DATA, " s)))
|
||||
#define HA_SECTION_START(s) __asm("section$start$__DATA$" s)
|
||||
#define HA_SECTION_STOP(s) __asm("section$end$__DATA$" s)
|
||||
#else
|
||||
#define HA_SECTION(s) __attribute__((__section__(s)))
|
||||
#define HA_SECTION_START(s)
|
||||
#define HA_SECTION_STOP(s)
|
||||
#endif
|
||||
|
||||
#else // obsolete linker below, let's just not force any section
|
||||
|
||||
#define HA_SECTION(s)
|
||||
#define HA_SECTION_START(s)
|
||||
#define HA_SECTION_STOP(s)
|
||||
|
||||
#endif // USE_OBSOLETE_LINKER
|
||||
|
||||
/* use this attribute on a variable to move it to the read_mostly section */
|
||||
#define __read_mostly HA_SECTION("read_mostly")
|
||||
|
||||
/* This allows gcc to know that some locations are never reached, for example
|
||||
* after a longjmp() in the Lua code, hence that some errors caught by such
|
||||
* methods cannot propagate further. This is important with gcc versions 6 and
|
||||
* above which can more aggressively detect null dereferences. The builtin
|
||||
* below was introduced in gcc 4.5, and before it we didn't care.
|
||||
*/
|
||||
#ifdef DEBUG_USE_ABORT
|
||||
#define my_unreachable() abort()
|
||||
#else
|
||||
#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
||||
#define my_unreachable() __builtin_unreachable()
|
||||
#else
|
||||
#define my_unreachable()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* This macro may be used to block constant propagation that lets the compiler
|
||||
* detect a possible NULL dereference on a variable resulting from an explicit
|
||||
* assignment in an impossible check. Sometimes a function is called which does
|
||||
* safety checks and returns NULL if safe conditions are not met. The place
|
||||
* where it's called cannot hit this condition and dereferencing the pointer
|
||||
* without first checking it will make the compiler emit a warning about a
|
||||
* "potential null pointer dereference" which is hard to work around. This
|
||||
* macro "washes" the pointer and prevents the compiler from emitting tests
|
||||
* branching to undefined instructions. It may only be used when the developer
|
||||
* is absolutely certain that the conditions are guaranteed and that the
|
||||
* pointer passed in argument cannot be NULL by design.
|
||||
*/
|
||||
#define ALREADY_CHECKED(p) do { asm("" : "=rm"(p) : "0"(p)); } while (0)
|
||||
|
||||
/* same as above but to be used to pass the input value to the output but
|
||||
* without letting the compiler know about its initial properties.
|
||||
*/
|
||||
#define DISGUISE(v) ({ typeof(v) __v = (v); ALREADY_CHECKED(__v); __v; })
|
||||
|
||||
/*
|
||||
* Gcc >= 3 provides the ability for the program to give hints to the
|
||||
* compiler about what branch of an if is most likely to be taken. This
|
||||
* helps the compiler produce the most compact critical paths, which is
|
||||
* generally better for the cache and to reduce the number of jumps.
|
||||
*/
|
||||
#if !defined(likely)
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 3)
|
||||
#define __builtin_expect(x,y) (x)
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#else
|
||||
#define likely(x) (__builtin_expect((x) != 0, 1))
|
||||
#define unlikely(x) (__builtin_expect((x) != 0, 0))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __GNUC_PREREQ__
|
||||
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
|
||||
#define __GNUC_PREREQ__(ma, mi) \
|
||||
(__GNUC__ > (ma) || __GNUC__ == (ma) && __GNUC_MINOR__ >= (mi))
|
||||
#else
|
||||
#define __GNUC_PREREQ__(ma, mi) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef offsetof
|
||||
#if __GNUC_PREREQ__(4, 1)
|
||||
#define offsetof(type, field) __builtin_offsetof(type, field)
|
||||
#else
|
||||
#define offsetof(type, field) \
|
||||
((size_t)(uintptr_t)((const volatile void *)&((type *)0)->field))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Some architectures have a double-word CAS, sometimes even dual-8 bytes.
|
||||
* Some architectures support unaligned accesses, others are fine with them
|
||||
* but only for non-atomic operations. Also mention those supporting unaligned
|
||||
* accesses and being little endian, and those where unaligned accesses are
|
||||
* known to be fast (almost as fast as aligned ones).
|
||||
*/
|
||||
#if defined(__x86_64__)
|
||||
#define HA_UNALIGNED
|
||||
#define HA_UNALIGNED_LE
|
||||
#define HA_UNALIGNED_LE64
|
||||
#define HA_UNALIGNED_FAST
|
||||
#define HA_UNALIGNED_ATOMIC
|
||||
#define HA_HAVE_CAS_DW
|
||||
#define HA_CAS_IS_8B
|
||||
#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
|
||||
#define HA_UNALIGNED
|
||||
#define HA_UNALIGNED_LE
|
||||
#define HA_UNALIGNED_ATOMIC
|
||||
#elif defined (__aarch64__) || defined(__ARM_ARCH_8A)
|
||||
#define HA_UNALIGNED
|
||||
#define HA_UNALIGNED_LE
|
||||
#define HA_UNALIGNED_LE64
|
||||
#define HA_UNALIGNED_FAST
|
||||
#define HA_HAVE_CAS_DW
|
||||
#define HA_CAS_IS_8B
|
||||
#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__))
|
||||
#define HA_UNALIGNED
|
||||
#define HA_UNALIGNED_LE
|
||||
#define HA_UNALIGNED_FAST
|
||||
#define HA_HAVE_CAS_DW
|
||||
#endif
|
||||
|
||||
|
||||
/* sets alignment for current field or variable */
|
||||
#ifndef ALIGNED
|
||||
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
||||
/* sets alignment only on architectures preventing unaligned atomic accesses */
|
||||
#ifndef MAYBE_ALIGNED
|
||||
#ifndef HA_UNALIGNED
|
||||
#define MAYBE_ALIGNED(x) ALIGNED(x)
|
||||
#else
|
||||
#define MAYBE_ALIGNED(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* sets alignment only on architectures preventing unaligned atomic accesses */
|
||||
#ifndef ATOMIC_ALIGNED
|
||||
#ifndef HA_UNALIGNED_ATOMIC
|
||||
#define ATOMIC_ALIGNED(x) ALIGNED(x)
|
||||
#else
|
||||
#define ATOMIC_ALIGNED(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* sets alignment for current field or variable only when threads are enabled.
|
||||
* Typically used to respect cache line alignment to avoid false sharing.
|
||||
*/
|
||||
#ifndef THREAD_ALIGNED
|
||||
#ifdef USE_THREAD
|
||||
#define THREAD_ALIGNED(x) __attribute__((aligned(x)))
|
||||
#else
|
||||
#define THREAD_ALIGNED(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* add a mandatory alignment for next fields in a structure */
|
||||
#ifndef ALWAYS_ALIGN
|
||||
#define ALWAYS_ALIGN(x) union { } ALIGNED(x)
|
||||
#endif
|
||||
|
||||
/* add an optional alignment for next fields in a structure, only for archs
|
||||
* which do not support unaligned accesses.
|
||||
*/
|
||||
#ifndef MAYBE_ALIGN
|
||||
#ifndef HA_UNALIGNED
|
||||
#define MAYBE_ALIGN(x) union { } ALIGNED(x)
|
||||
#else
|
||||
#define MAYBE_ALIGN(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* add an optional alignment for next fields in a structure, only for archs
|
||||
* which do not support unaligned accesses for atomic operations.
|
||||
*/
|
||||
#ifndef ATOMIC_ALIGN
|
||||
#ifndef HA_UNALIGNED_ATOMIC
|
||||
#define ATOMIC_ALIGN(x) union { } ALIGNED(x)
|
||||
#else
|
||||
#define ATOMIC_ALIGN(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* add an optional alignment for next fields in a structure, only when threads
|
||||
* are enabled. Typically used to respect cache line alignment to avoid false
|
||||
* sharing.
|
||||
*/
|
||||
#ifndef THREAD_ALIGN
|
||||
#ifdef USE_THREAD
|
||||
#define THREAD_ALIGN(x) union { } ALIGNED(x)
|
||||
#else
|
||||
#define THREAD_ALIGN(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The THREAD_LOCAL type attribute defines thread-local storage and is defined
|
||||
* to __thread when threads are enabled or empty when disabled.
|
||||
*/
|
||||
#ifdef USE_THREAD
|
||||
#define THREAD_LOCAL __thread
|
||||
#else
|
||||
#define THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
/* The __decl_thread() statement is shows the argument when threads are enabled
|
||||
* or hides it when disabled. The purpose is to condition the presence of some
|
||||
* variables or struct members to the fact that threads are enabled, without
|
||||
* having to enclose them inside a #ifdef USE_THREAD/#endif clause.
|
||||
*/
|
||||
#ifdef USE_THREAD
|
||||
#define __decl_thread(decl) decl
|
||||
#else
|
||||
#define __decl_thread(decl)
|
||||
#endif
|
||||
|
||||
/* clang has a __has_feature() macro which reports true/false on a number of
|
||||
* internally supported features. Let's make sure this macro is always defined
|
||||
* and returns zero when not supported.
|
||||
*/
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#endif /* _HAPROXY_COMPILER_H */
|
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/http-t.h
|
||||
*
|
||||
* Version-agnostic and implementation-agnostic HTTP protocol definitions.
|
||||
*
|
||||
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_HTTP_T_H
|
||||
#define _HAPROXY_HTTP_T_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <haproxy/buf-t.h>
|
||||
|
||||
/*
|
||||
* some macros mainly used when parsing header fields.
|
||||
* from RFC7230:
|
||||
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
* SEP = one of the 17 defined separators or SP or HT
|
||||
* LWS = CR, LF, SP or HT
|
||||
* SPHT = SP or HT. Use this macro and not a boolean expression for best speed.
|
||||
* CRLF = CR or LF. Use this macro and not a boolean expression for best speed.
|
||||
* token = any CHAR except CTL or SEP. Use this macro and not a boolean expression for best speed.
|
||||
*
|
||||
* added for ease of use:
|
||||
* ver_token = 'H', 'P', 'T', '/', '.', and digits.
|
||||
*/
|
||||
#define HTTP_FLG_CTL 0x01
|
||||
#define HTTP_FLG_SEP 0x02
|
||||
#define HTTP_FLG_LWS 0x04
|
||||
#define HTTP_FLG_SPHT 0x08
|
||||
#define HTTP_FLG_CRLF 0x10
|
||||
#define HTTP_FLG_TOK 0x20
|
||||
#define HTTP_FLG_VER 0x40
|
||||
#define HTTP_FLG_DIG 0x80
|
||||
|
||||
#define HTTP_IS_CTL(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_CTL)
|
||||
#define HTTP_IS_SEP(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_SEP)
|
||||
#define HTTP_IS_LWS(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_LWS)
|
||||
#define HTTP_IS_SPHT(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_SPHT)
|
||||
#define HTTP_IS_CRLF(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_CRLF)
|
||||
#define HTTP_IS_TOKEN(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_TOK)
|
||||
#define HTTP_IS_VER_TOKEN(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_VER)
|
||||
#define HTTP_IS_DIGIT(x) (http_char_classes[(uint8_t)(x)] & HTTP_FLG_DIG)
|
||||
|
||||
/* Known HTTP methods */
|
||||
enum http_meth_t {
|
||||
HTTP_METH_OPTIONS,
|
||||
HTTP_METH_GET,
|
||||
HTTP_METH_HEAD,
|
||||
HTTP_METH_POST,
|
||||
HTTP_METH_PUT,
|
||||
HTTP_METH_DELETE,
|
||||
HTTP_METH_TRACE,
|
||||
HTTP_METH_CONNECT,
|
||||
HTTP_METH_OTHER, /* Must be the last entry */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Known HTTP authentication schemes */
|
||||
enum ht_auth_m {
|
||||
HTTP_AUTH_WRONG = -1, /* missing or unknown */
|
||||
HTTP_AUTH_UNKNOWN = 0,
|
||||
HTTP_AUTH_BASIC,
|
||||
HTTP_AUTH_DIGEST,
|
||||
} __attribute__((packed));
|
||||
|
||||
/* All implemented HTTP status codes */
|
||||
enum {
|
||||
HTTP_ERR_200 = 0,
|
||||
HTTP_ERR_400,
|
||||
HTTP_ERR_401,
|
||||
HTTP_ERR_403,
|
||||
HTTP_ERR_404,
|
||||
HTTP_ERR_405,
|
||||
HTTP_ERR_407,
|
||||
HTTP_ERR_408,
|
||||
HTTP_ERR_410,
|
||||
HTTP_ERR_413,
|
||||
HTTP_ERR_421,
|
||||
HTTP_ERR_425,
|
||||
HTTP_ERR_429,
|
||||
HTTP_ERR_500,
|
||||
HTTP_ERR_501,
|
||||
HTTP_ERR_502,
|
||||
HTTP_ERR_503,
|
||||
HTTP_ERR_504,
|
||||
HTTP_ERR_SIZE
|
||||
};
|
||||
|
||||
/* Note: the strings below make use of chunks. Chunks may carry an allocated
|
||||
* size in addition to the length. The size counts from the beginning (str)
|
||||
* to the end. If the size is unknown, it MUST be zero, in which case the
|
||||
* sample will automatically be duplicated when a change larger than <len> has
|
||||
* to be performed. Thus it is safe to always set size to zero.
|
||||
*/
|
||||
struct http_meth {
|
||||
enum http_meth_t meth;
|
||||
struct buffer str;
|
||||
};
|
||||
|
||||
struct http_auth_data {
|
||||
enum ht_auth_m method; /* one of HTTP_AUTH_* */
|
||||
/* 7 bytes unused here */
|
||||
struct buffer method_data; /* points to the creditial part from 'Authorization:' header */
|
||||
char *user, *pass; /* extracted username & password */
|
||||
};
|
||||
|
||||
enum http_etag_type {
|
||||
ETAG_INVALID = 0,
|
||||
ETAG_STRONG,
|
||||
ETAG_WEAK
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_HTTP_T_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -1,469 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/intops.h
|
||||
* Functions for integer operations.
|
||||
*
|
||||
* Copyright (C) 2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_INTOPS_H
|
||||
#define _HAPROXY_INTOPS_H
|
||||
|
||||
#include <haproxy/api.h>
|
||||
|
||||
/* Multiply the two 32-bit operands and shift the 64-bit result right 32 bits.
|
||||
* This is used to compute fixed ratios by setting one of the operands to
|
||||
* (2^32*ratio).
|
||||
*/
|
||||
static inline unsigned int mul32hi(unsigned int a, unsigned int b)
|
||||
{
|
||||
return ((unsigned long long)a * b + a) >> 32;
|
||||
}
|
||||
|
||||
/* gcc does not know when it can safely divide 64 bits by 32 bits. Use this
|
||||
* function when you know for sure that the result fits in 32 bits, because
|
||||
* it is optimal on x86 and on 64bit processors.
|
||||
*/
|
||||
static inline unsigned int div64_32(unsigned long long o1, unsigned int o2)
|
||||
{
|
||||
unsigned long long result;
|
||||
#ifdef __i386__
|
||||
asm("divl %2"
|
||||
: "=A" (result)
|
||||
: "A"(o1), "rm"(o2));
|
||||
#else
|
||||
result = o1 / o2;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* rotate left a 64-bit integer by <bits:[0-5]> bits */
|
||||
static inline uint64_t rotl64(uint64_t v, uint8_t bits)
|
||||
{
|
||||
#if !defined(__ARM_ARCH_8A) && !defined(__x86_64__)
|
||||
bits &= 63;
|
||||
#endif
|
||||
v = (v << bits) | (v >> (-bits & 63));
|
||||
return v;
|
||||
}
|
||||
|
||||
/* rotate right a 64-bit integer by <bits:[0-5]> bits */
|
||||
static inline uint64_t rotr64(uint64_t v, uint8_t bits)
|
||||
{
|
||||
#if !defined(__ARM_ARCH_8A) && !defined(__x86_64__)
|
||||
bits &= 63;
|
||||
#endif
|
||||
v = (v >> bits) | (v << (-bits & 63));
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Simple popcountl implementation. It returns the number of ones in a word.
|
||||
* Described here : https://graphics.stanford.edu/~seander/bithacks.html
|
||||
*/
|
||||
static inline unsigned int my_popcountl(unsigned long a)
|
||||
{
|
||||
a = a - ((a >> 1) & ~0UL/3);
|
||||
a = (a & ~0UL/15*3) + ((a >> 2) & ~0UL/15*3);
|
||||
a = (a + (a >> 4)) & ~0UL/255*15;
|
||||
return (unsigned long)(a * (~0UL/255)) >> (sizeof(unsigned long) - 1) * 8;
|
||||
}
|
||||
|
||||
/* returns non-zero if <a> has at least 2 bits set */
|
||||
static inline unsigned long atleast2(unsigned long a)
|
||||
{
|
||||
return a & (a - 1);
|
||||
}
|
||||
|
||||
/* Simple ffs implementation. It returns the position of the lowest bit set to
|
||||
* one, starting at 1. It is illegal to call it with a==0 (undefined result).
|
||||
*/
|
||||
static inline unsigned int my_ffsl(unsigned long a)
|
||||
{
|
||||
unsigned long cnt;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
__asm__("bsf %1,%0\n" : "=r" (cnt) : "rm" (a));
|
||||
cnt++;
|
||||
#else
|
||||
|
||||
cnt = 1;
|
||||
#if LONG_MAX > 0x7FFFFFFFL /* 64bits */
|
||||
if (!(a & 0xFFFFFFFFUL)) {
|
||||
a >>= 32;
|
||||
cnt += 32;
|
||||
}
|
||||
#endif
|
||||
if (!(a & 0XFFFFU)) {
|
||||
a >>= 16;
|
||||
cnt += 16;
|
||||
}
|
||||
if (!(a & 0XFF)) {
|
||||
a >>= 8;
|
||||
cnt += 8;
|
||||
}
|
||||
if (!(a & 0xf)) {
|
||||
a >>= 4;
|
||||
cnt += 4;
|
||||
}
|
||||
if (!(a & 0x3)) {
|
||||
a >>= 2;
|
||||
cnt += 2;
|
||||
}
|
||||
if (!(a & 0x1)) {
|
||||
cnt += 1;
|
||||
}
|
||||
#endif /* x86_64 */
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Simple fls implementation. It returns the position of the highest bit set to
|
||||
* one, starting at 1. It is illegal to call it with a==0 (undefined result).
|
||||
*/
|
||||
static inline unsigned int my_flsl(unsigned long a)
|
||||
{
|
||||
unsigned long cnt;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
__asm__("bsr %1,%0\n" : "=r" (cnt) : "rm" (a));
|
||||
cnt++;
|
||||
#else
|
||||
|
||||
cnt = 1;
|
||||
#if LONG_MAX > 0x7FFFFFFFUL /* 64bits */
|
||||
if (a & 0xFFFFFFFF00000000UL) {
|
||||
a >>= 32;
|
||||
cnt += 32;
|
||||
}
|
||||
#endif
|
||||
if (a & 0XFFFF0000U) {
|
||||
a >>= 16;
|
||||
cnt += 16;
|
||||
}
|
||||
if (a & 0XFF00) {
|
||||
a >>= 8;
|
||||
cnt += 8;
|
||||
}
|
||||
if (a & 0xf0) {
|
||||
a >>= 4;
|
||||
cnt += 4;
|
||||
}
|
||||
if (a & 0xc) {
|
||||
a >>= 2;
|
||||
cnt += 2;
|
||||
}
|
||||
if (a & 0x2) {
|
||||
cnt += 1;
|
||||
}
|
||||
#endif /* x86_64 */
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Build a word with the <bits> lower bits set (reverse of my_popcountl) */
|
||||
static inline unsigned long nbits(int bits)
|
||||
{
|
||||
if (--bits < 0)
|
||||
return 0;
|
||||
else
|
||||
return (2UL << bits) - 1;
|
||||
}
|
||||
|
||||
/* Turns 64-bit value <a> from host byte order to network byte order.
|
||||
* The principle consists in letting the compiler detect we're playing
|
||||
* with a union and simplify most or all operations. The asm-optimized
|
||||
* htonl() version involving bswap (x86) / rev (arm) / other is a single
|
||||
* operation on little endian, or a NOP on big-endian. In both cases,
|
||||
* this lets the compiler "see" that we're rebuilding a 64-bit word from
|
||||
* two 32-bit quantities that fit into a 32-bit register. In big endian,
|
||||
* the whole code is optimized out. In little endian, with a decent compiler,
|
||||
* a few bswap and 2 shifts are left, which is the minimum acceptable.
|
||||
*/
|
||||
static inline unsigned long long my_htonll(unsigned long long a)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
__asm__ volatile("bswapq %0" : "=r"(a) : "0"(a));
|
||||
return a;
|
||||
#else
|
||||
union {
|
||||
struct {
|
||||
unsigned int w1;
|
||||
unsigned int w2;
|
||||
} by32;
|
||||
unsigned long long by64;
|
||||
} w = { .by64 = a };
|
||||
return ((unsigned long long)htonl(w.by32.w1) << 32) | htonl(w.by32.w2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Turns 64-bit value <a> from network byte order to host byte order. */
|
||||
static inline unsigned long long my_ntohll(unsigned long long a)
|
||||
{
|
||||
return my_htonll(a);
|
||||
}
|
||||
|
||||
/* sets bit <bit> into map <map>, which must be long-aligned */
|
||||
static inline void ha_bit_set(unsigned long bit, long *map)
|
||||
{
|
||||
map[bit / (8 * sizeof(*map))] |= 1UL << (bit & (8 * sizeof(*map) - 1));
|
||||
}
|
||||
|
||||
/* clears bit <bit> from map <map>, which must be long-aligned */
|
||||
static inline void ha_bit_clr(unsigned long bit, long *map)
|
||||
{
|
||||
map[bit / (8 * sizeof(*map))] &= ~(1UL << (bit & (8 * sizeof(*map) - 1)));
|
||||
}
|
||||
|
||||
/* flips bit <bit> from map <map>, which must be long-aligned */
|
||||
static inline void ha_bit_flip(unsigned long bit, long *map)
|
||||
{
|
||||
map[bit / (8 * sizeof(*map))] ^= 1UL << (bit & (8 * sizeof(*map) - 1));
|
||||
}
|
||||
|
||||
/* returns non-zero if bit <bit> from map <map> is set, otherwise 0 */
|
||||
static inline int ha_bit_test(unsigned long bit, const long *map)
|
||||
{
|
||||
return !!(map[bit / (8 * sizeof(*map))] & 1UL << (bit & (8 * sizeof(*map) - 1)));
|
||||
}
|
||||
|
||||
/* hash a 32-bit integer to another 32-bit integer. This code may be large when
|
||||
* inlined, use full_hash() instead.
|
||||
*/
|
||||
static inline unsigned int __full_hash(unsigned int a)
|
||||
{
|
||||
/* This function is one of Bob Jenkins' full avalanche hashing
|
||||
* functions, which when provides quite a good distribution for little
|
||||
* input variations. The result is quite suited to fit over a 32-bit
|
||||
* space with enough variations so that a randomly picked number falls
|
||||
* equally before any server position.
|
||||
* Check http://burtleburtle.net/bob/hash/integer.html for more info.
|
||||
*/
|
||||
a = (a+0x7ed55d16) + (a<<12);
|
||||
a = (a^0xc761c23c) ^ (a>>19);
|
||||
a = (a+0x165667b1) + (a<<5);
|
||||
a = (a+0xd3a2646c) ^ (a<<9);
|
||||
a = (a+0xfd7046c5) + (a<<3);
|
||||
a = (a^0xb55a4f09) ^ (a>>16);
|
||||
|
||||
/* ensure values are better spread all around the tree by multiplying
|
||||
* by a large prime close to 3/4 of the tree.
|
||||
*/
|
||||
return a * 3221225473U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return integer equivalent of character <c> for a hex digit (0-9, a-f, A-F),
|
||||
* otherwise -1. This compact form helps gcc produce efficient code.
|
||||
*/
|
||||
static inline int hex2i(int c)
|
||||
{
|
||||
if ((unsigned char)(c -= '0') > 9) {
|
||||
if ((unsigned char)(c -= 'A' - '0') > 5 &&
|
||||
(unsigned char)(c -= 'a' - 'A') > 5)
|
||||
c = -11;
|
||||
c += 10;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* This one is 6 times faster than strtoul() on athlon, but does
|
||||
* no check at all.
|
||||
*/
|
||||
static inline unsigned int __str2ui(const char *s)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
while (*s) {
|
||||
i = i * 10 - '0';
|
||||
i += (unsigned char)*s++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* This one is 5 times faster than strtoul() on athlon with checks.
|
||||
* It returns the value of the number composed of all valid digits read.
|
||||
*/
|
||||
static inline unsigned int __str2uic(const char *s)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
unsigned int j;
|
||||
|
||||
while (1) {
|
||||
j = (*s++) - '0';
|
||||
if (j > 9)
|
||||
break;
|
||||
i *= 10;
|
||||
i += j;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* This one is 28 times faster than strtoul() on athlon, but does
|
||||
* no check at all!
|
||||
*/
|
||||
static inline unsigned int __strl2ui(const char *s, int len)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
while (len-- > 0) {
|
||||
i = i * 10 - '0';
|
||||
i += (unsigned char)*s++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* This one is 7 times faster than strtoul() on athlon with checks.
|
||||
* It returns the value of the number composed of all valid digits read.
|
||||
*/
|
||||
static inline unsigned int __strl2uic(const char *s, int len)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
unsigned int j, k;
|
||||
|
||||
while (len-- > 0) {
|
||||
j = (*s++) - '0';
|
||||
k = i * 10;
|
||||
if (j > 9)
|
||||
break;
|
||||
i = k + j;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* This function reads an unsigned integer from the string pointed to by <s>
|
||||
* and returns it. The <s> pointer is adjusted to point to the first unread
|
||||
* char. The function automatically stops at <end>.
|
||||
*/
|
||||
static inline unsigned int __read_uint(const char **s, const char *end)
|
||||
{
|
||||
const char *ptr = *s;
|
||||
unsigned int i = 0;
|
||||
unsigned int j, k;
|
||||
|
||||
while (ptr < end) {
|
||||
j = *ptr - '0';
|
||||
k = i * 10;
|
||||
if (j > 9)
|
||||
break;
|
||||
i = k + j;
|
||||
ptr++;
|
||||
}
|
||||
*s = ptr;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* returns the number of bytes needed to encode <v> as a varint. Be careful, use
|
||||
* it only with constants as it generates a large code (typ. 180 bytes). Use the
|
||||
* varint_bytes() version instead in case of doubt.
|
||||
*/
|
||||
static inline int __varint_bytes(uint64_t v)
|
||||
{
|
||||
switch (v) {
|
||||
case 0x0000000000000000 ... 0x00000000000000ef: return 1;
|
||||
case 0x00000000000000f0 ... 0x00000000000008ef: return 2;
|
||||
case 0x00000000000008f0 ... 0x00000000000408ef: return 3;
|
||||
case 0x00000000000408f0 ... 0x00000000020408ef: return 4;
|
||||
case 0x00000000020408f0 ... 0x00000001020408ef: return 5;
|
||||
case 0x00000001020408f0 ... 0x00000081020408ef: return 6;
|
||||
case 0x00000081020408f0 ... 0x00004081020408ef: return 7;
|
||||
case 0x00004081020408f0 ... 0x00204081020408ef: return 8;
|
||||
case 0x00204081020408f0 ... 0x10204081020408ef: return 9;
|
||||
default: return 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_INTOPS_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/list-t.h
|
||||
* Circular list manipulation types definitions
|
||||
*
|
||||
* Copyright (C) 2002-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_LIST_T_H
|
||||
#define _HAPROXY_LIST_T_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 */
|
||||
};
|
||||
|
||||
/* This is similar to struct list, but we want to be sure the compiler will
|
||||
* yell at you if you use macroes for one when you're using the other. You have
|
||||
* to expicitely cast if that's really what you want to do.
|
||||
*/
|
||||
struct mt_list {
|
||||
struct mt_list *next;
|
||||
struct mt_list *prev;
|
||||
};
|
||||
|
||||
|
||||
/* a back-ref is a pointer to a target list entry. It is used to detect when an
|
||||
* element being deleted is currently being tracked by another user. The best
|
||||
* example is a user dumping the session table. The table does not fit in the
|
||||
* output buffer so we have to set a mark on a session and go on later. But if
|
||||
* that marked session gets deleted, we don't want the user's pointer to go in
|
||||
* the wild. So we can simply link this user's request to the list of this
|
||||
* session's users, and put a pointer to the list element in ref, that will be
|
||||
* used as the mark for next iteration.
|
||||
*/
|
||||
struct bref {
|
||||
struct list users;
|
||||
struct list *ref; /* pointer to the target's list entry */
|
||||
};
|
||||
|
||||
/* a word list is a generic list with a pointer to a string in each element. */
|
||||
struct wordlist {
|
||||
struct list list;
|
||||
char *s;
|
||||
};
|
||||
|
||||
/* this is the same as above with an additional pointer to a condition. */
|
||||
struct cond_wordlist {
|
||||
struct list list;
|
||||
void *cond;
|
||||
char *s;
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_LIST_T_H */
|
@ -1,804 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/list.h
|
||||
* Circular list manipulation macros and functions.
|
||||
*
|
||||
* Copyright (C) 2002-2020 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* 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 _HAPROXY_LIST_H
|
||||
#define _HAPROXY_LIST_H
|
||||
|
||||
#include <haproxy/api.h>
|
||||
|
||||
/* 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); })
|
||||
|
||||
/* adds the contents of a list <old> at the beginning of another list <new>. The old list head remains untouched. */
|
||||
#define LIST_SPLICE(new, old) do { \
|
||||
if (!LIST_ISEMPTY(old)) { \
|
||||
(old)->p->n = (new)->n; (old)->n->p = (new); \
|
||||
(new)->n->p = (old)->p; (new)->n = (old)->n; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* adds the contents of a list whose first element is <old> and last one is
|
||||
* <old->prev> at the end of another list <new>. The old list DOES NOT have
|
||||
* any head here.
|
||||
*/
|
||||
#define LIST_SPLICE_END_DETACHED(new, old) do { \
|
||||
typeof(new) __t; \
|
||||
(new)->p->n = (old); \
|
||||
(old)->p->n = (new); \
|
||||
__t = (old)->p; \
|
||||
(old)->p = (new)->p; \
|
||||
(new)->p = __t; \
|
||||
} while (0)
|
||||
|
||||
/* 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); })
|
||||
|
||||
/* removes an element from a list, initializes it and returns it.
|
||||
* This is faster than LIST_DELETE+LIST_INIT as we avoid reloading the pointers.
|
||||
*/
|
||||
#define LIST_DEL_INIT(el) ({ \
|
||||
typeof(el) __ret = (el); \
|
||||
typeof(__ret->n) __n = __ret->n; \
|
||||
typeof(__ret->p) __p = __ret->p; \
|
||||
__n->p = __p; __p->n = __n; \
|
||||
__ret->n = __ret->p = __ret; \
|
||||
__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))
|
||||
|
||||
/* checks if the list element <el> was added to a list or not. This only
|
||||
* works when detached elements are reinitialized (using LIST_DEL_INIT)
|
||||
*/
|
||||
#define LIST_INLIST(el) ((el)->n != (el))
|
||||
|
||||
/* 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))
|
||||
|
||||
/*
|
||||
* Same as list_for_each_entry but starting from current point
|
||||
* Iterates <item> through the list starting from <item>
|
||||
* It's basically the same macro but without initializing item to the head of
|
||||
* the list.
|
||||
*/
|
||||
#define list_for_each_entry_from(item, list_head, member) \
|
||||
for ( ; &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))
|
||||
|
||||
|
||||
/*
|
||||
* Same as list_for_each_entry_safe but starting from current point
|
||||
* Iterates <item> through the list starting from <item>
|
||||
* It's basically the same macro but without initializing item to the head of
|
||||
* the list.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from(item, back, list_head, member) \
|
||||
for (back = LIST_ELEM(item->member.n, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = back, back = LIST_ELEM(back->member.n, typeof(back), member))
|
||||
|
||||
/*
|
||||
* Iterate backwards <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_rev(cur_acl, known_acl, list) { ... };
|
||||
*/
|
||||
#define list_for_each_entry_rev(item, list_head, member) \
|
||||
for (item = LIST_ELEM((list_head)->p, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = LIST_ELEM(item->member.p, typeof(item), member))
|
||||
|
||||
/*
|
||||
* Same as list_for_each_entry_rev but starting from current point
|
||||
* Iterate backwards <item> through the list starting from <item>
|
||||
* It's basically the same macro but without initializing item to the head of
|
||||
* the list.
|
||||
*/
|
||||
#define list_for_each_entry_from_rev(item, list_head, member) \
|
||||
for ( ; &item->member != (list_head); \
|
||||
item = LIST_ELEM(item->member.p, typeof(item), member))
|
||||
|
||||
/*
|
||||
* Iterate backwards <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_rev(cur_acl, tmp, known_acl, list) { ... };
|
||||
*/
|
||||
#define list_for_each_entry_safe_rev(item, back, list_head, member) \
|
||||
for (item = LIST_ELEM((list_head)->p, typeof(item), member), \
|
||||
back = LIST_ELEM(item->member.p, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = back, back = LIST_ELEM(back->member.p, typeof(back), member))
|
||||
|
||||
/*
|
||||
* Same as list_for_each_entry_safe_rev but starting from current point
|
||||
* Iterate backwards <item> through the list starting from <item>
|
||||
* It's basically the same macro but without initializing item to the head of
|
||||
* the list.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from_rev(item, back, list_head, member) \
|
||||
for (back = LIST_ELEM(item->member.p, typeof(item), member); \
|
||||
&item->member != (list_head); \
|
||||
item = back, back = LIST_ELEM(back->member.p, typeof(back), member))
|
||||
|
||||
|
||||
/*
|
||||
* Locked version of list manipulation macros.
|
||||
* It is OK to use those concurrently from multiple threads, as long as the
|
||||
* list is only used with the locked variants.
|
||||
*/
|
||||
#define MT_LIST_BUSY ((struct mt_list *)1)
|
||||
|
||||
/*
|
||||
* Add an item at the beginning of a list.
|
||||
* Returns 1 if we added the item, 0 otherwise (because it was already in a
|
||||
* list).
|
||||
*/
|
||||
#define MT_LIST_TRY_INSERT(_lh, _el) \
|
||||
({ \
|
||||
int _ret = 0; \
|
||||
struct mt_list *lh = (_lh), *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n, *n2; \
|
||||
struct mt_list *p, *p2; \
|
||||
n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
p = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) { \
|
||||
(lh)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
n2 = _HA_ATOMIC_XCHG(&el->next, MT_LIST_BUSY); \
|
||||
if (n2 != el) { /* element already linked */ \
|
||||
if (n2 != MT_LIST_BUSY) \
|
||||
el->next = n2; \
|
||||
n->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
lh->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
if (n2 == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
break; \
|
||||
} \
|
||||
p2 = _HA_ATOMIC_XCHG(&el->prev, MT_LIST_BUSY); \
|
||||
if (p2 != el) { \
|
||||
if (p2 != MT_LIST_BUSY) \
|
||||
el->prev = p2; \
|
||||
n->prev = p; \
|
||||
el->next = el; \
|
||||
__ha_barrier_store(); \
|
||||
lh->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
if (p2 == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
break; \
|
||||
} \
|
||||
(el)->next = n; \
|
||||
(el)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
n->prev = (el); \
|
||||
__ha_barrier_store(); \
|
||||
p->next = (el); \
|
||||
__ha_barrier_store(); \
|
||||
_ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Add an item at the end of a list.
|
||||
* Returns 1 if we added the item, 0 otherwise (because it was already in a
|
||||
* list).
|
||||
*/
|
||||
#define MT_LIST_TRY_APPEND(_lh, _el) \
|
||||
({ \
|
||||
int _ret = 0; \
|
||||
struct mt_list *lh = (_lh), *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n, *n2; \
|
||||
struct mt_list *p, *p2; \
|
||||
p = _HA_ATOMIC_XCHG(&(lh)->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
n = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) { \
|
||||
(lh)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
p2 = _HA_ATOMIC_XCHG(&el->prev, MT_LIST_BUSY); \
|
||||
if (p2 != el) { \
|
||||
if (p2 != MT_LIST_BUSY) \
|
||||
el->prev = p2; \
|
||||
p->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
lh->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
if (p2 == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
break; \
|
||||
} \
|
||||
n2 = _HA_ATOMIC_XCHG(&el->next, MT_LIST_BUSY); \
|
||||
if (n2 != el) { /* element already linked */ \
|
||||
if (n2 != MT_LIST_BUSY) \
|
||||
el->next = n2; \
|
||||
p->next = n; \
|
||||
el->prev = el; \
|
||||
__ha_barrier_store(); \
|
||||
lh->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
if (n2 == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
break; \
|
||||
} \
|
||||
(el)->next = n; \
|
||||
(el)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
p->next = (el); \
|
||||
__ha_barrier_store(); \
|
||||
n->prev = (el); \
|
||||
__ha_barrier_store(); \
|
||||
_ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Add an item at the beginning of a list.
|
||||
* It is assumed the element can't already be in a list, so it isn't checked.
|
||||
*/
|
||||
#define MT_LIST_INSERT(_lh, _el) \
|
||||
({ \
|
||||
int _ret = 0; \
|
||||
struct mt_list *lh = (_lh), *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n; \
|
||||
struct mt_list *p; \
|
||||
n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
p = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) { \
|
||||
(lh)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
(el)->next = n; \
|
||||
(el)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
n->prev = (el); \
|
||||
__ha_barrier_store(); \
|
||||
p->next = (el); \
|
||||
__ha_barrier_store(); \
|
||||
_ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Add an item at the end of a list.
|
||||
* It is assumed the element can't already be in a list, so it isn't checked
|
||||
*/
|
||||
#define MT_LIST_APPEND(_lh, _el) \
|
||||
({ \
|
||||
int _ret = 0; \
|
||||
struct mt_list *lh = (_lh), *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n; \
|
||||
struct mt_list *p; \
|
||||
p = _HA_ATOMIC_XCHG(&(lh)->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
n = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) { \
|
||||
(lh)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
(el)->next = n; \
|
||||
(el)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
p->next = (el); \
|
||||
__ha_barrier_store(); \
|
||||
n->prev = (el); \
|
||||
__ha_barrier_store(); \
|
||||
_ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Detach a list from its head. A pointer to the first element is returned
|
||||
* and the list is closed. If the list was empty, NULL is returned. This may
|
||||
* exclusively be used with lists modified by MT_LIST_TRY_INSERT/MT_LIST_TRY_APPEND. This
|
||||
* is incompatible with MT_LIST_DELETE run concurrently.
|
||||
* If there's at least one element, the next of the last element will always
|
||||
* be NULL.
|
||||
*/
|
||||
#define MT_LIST_BEHEAD(_lh) ({ \
|
||||
struct mt_list *lh = (_lh); \
|
||||
struct mt_list *_n; \
|
||||
struct mt_list *_p; \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
_p = _HA_ATOMIC_XCHG(&(lh)->prev, MT_LIST_BUSY); \
|
||||
if (_p == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
if (_p == (lh)) { \
|
||||
(lh)->prev = _p; \
|
||||
__ha_barrier_store(); \
|
||||
_n = NULL; \
|
||||
break; \
|
||||
} \
|
||||
_n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \
|
||||
if (_n == MT_LIST_BUSY) { \
|
||||
(lh)->prev = _p; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
if (_n == (lh)) { \
|
||||
(lh)->next = _n; \
|
||||
(lh)->prev = _p; \
|
||||
__ha_barrier_store(); \
|
||||
_n = NULL; \
|
||||
break; \
|
||||
} \
|
||||
(lh)->next = (lh); \
|
||||
(lh)->prev = (lh); \
|
||||
__ha_barrier_store(); \
|
||||
_n->prev = _p; \
|
||||
__ha_barrier_store(); \
|
||||
_p->next = NULL; \
|
||||
__ha_barrier_store(); \
|
||||
break; \
|
||||
} \
|
||||
(_n); \
|
||||
})
|
||||
|
||||
|
||||
/* Remove an item from a list.
|
||||
* Returns 1 if we removed the item, 0 otherwise (because it was in no list).
|
||||
*/
|
||||
#define MT_LIST_DELETE(_el) \
|
||||
({ \
|
||||
int _ret = 0; \
|
||||
struct mt_list *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n, *n2; \
|
||||
struct mt_list *p, *p2 = NULL; \
|
||||
n = _HA_ATOMIC_XCHG(&(el)->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
p = _HA_ATOMIC_XCHG(&(el)->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) { \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
if (p != (el)) { \
|
||||
p2 = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY); \
|
||||
if (p2 == MT_LIST_BUSY) { \
|
||||
(el)->prev = p; \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
if (n != (el)) { \
|
||||
n2 = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \
|
||||
if (n2 == MT_LIST_BUSY) { \
|
||||
if (p2 != NULL) \
|
||||
p->next = p2; \
|
||||
(el)->prev = p; \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
n->prev = p; \
|
||||
p->next = n; \
|
||||
if (p != (el) && n != (el)) \
|
||||
_ret = 1; \
|
||||
__ha_barrier_store(); \
|
||||
(el)->prev = (el); \
|
||||
(el)->next = (el); \
|
||||
__ha_barrier_store(); \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
|
||||
/* Remove the first element from the list, and return it */
|
||||
#define MT_LIST_POP(_lh, pt, el) \
|
||||
({ \
|
||||
void *_ret; \
|
||||
struct mt_list *lh = (_lh); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n, *n2; \
|
||||
struct mt_list *p, *p2; \
|
||||
n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
if (n == (lh)) { \
|
||||
(lh)->next = lh; \
|
||||
__ha_barrier_store(); \
|
||||
_ret = NULL; \
|
||||
break; \
|
||||
} \
|
||||
p = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) { \
|
||||
(lh)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
n2 = _HA_ATOMIC_XCHG(&n->next, MT_LIST_BUSY); \
|
||||
if (n2 == MT_LIST_BUSY) { \
|
||||
n->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
(lh)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
p2 = _HA_ATOMIC_XCHG(&n2->prev, MT_LIST_BUSY); \
|
||||
if (p2 == MT_LIST_BUSY) { \
|
||||
n->next = n2; \
|
||||
n->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
(lh)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
(lh)->next = n2; \
|
||||
(n2)->prev = (lh); \
|
||||
__ha_barrier_store(); \
|
||||
(n)->prev = (n); \
|
||||
(n)->next = (n); \
|
||||
__ha_barrier_store(); \
|
||||
_ret = MT_LIST_ELEM(n, pt, el); \
|
||||
break; \
|
||||
} \
|
||||
(_ret); \
|
||||
})
|
||||
|
||||
#define MT_LIST_HEAD(a) ((void *)(&(a)))
|
||||
|
||||
#define MT_LIST_INIT(l) ((l)->next = (l)->prev = (l))
|
||||
|
||||
#define MT_LIST_HEAD_INIT(l) { &l, &l }
|
||||
/* 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: MT_LIST_ELEM(cur_node->args.next, struct node *, args)
|
||||
*/
|
||||
#define MT_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 MT_LIST_ISEMPTY(lh) ((lh)->next == (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: MT_LIST_NEXT(args, struct node *, list)
|
||||
*/
|
||||
#define MT_LIST_NEXT(lh, pt, el) (MT_LIST_ELEM((lh)->next, 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 MT_LIST_PREV
|
||||
#define MT_LIST_PREV(lh, pt, el) (MT_LIST_ELEM((lh)->prev, pt, el))
|
||||
|
||||
/* checks if the list element <el> was added to a list or not. This only
|
||||
* works when detached elements are reinitialized (using LIST_DEL_INIT)
|
||||
*/
|
||||
#define MT_LIST_INLIST(el) ((el)->next != (el))
|
||||
|
||||
/* Lock an element in the list, to be sure it won't be removed.
|
||||
* It needs to be synchronized somehow to be sure it's not removed
|
||||
* from the list in the meanwhile.
|
||||
* This returns a struct mt_list, that will be needed at unlock time.
|
||||
*/
|
||||
#define MT_LIST_LOCK_ELT(_el) \
|
||||
({ \
|
||||
struct mt_list ret; \
|
||||
struct mt_liet *el = (_el); \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n, *n2; \
|
||||
struct mt_list *p, *p2 = NULL; \
|
||||
n = _HA_ATOMIC_XCHG(&(el)->next, MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
p = _HA_ATOMIC_XCHG(&(el)->prev, MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) { \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
if (p != (el)) { \
|
||||
p2 = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY);\
|
||||
if (p2 == MT_LIST_BUSY) { \
|
||||
(el)->prev = p; \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
if (n != (el)) { \
|
||||
n2 = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY);\
|
||||
if (n2 == MT_LIST_BUSY) { \
|
||||
if (p2 != NULL) \
|
||||
p->next = p2; \
|
||||
(el)->prev = p; \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
ret.next = n; \
|
||||
ret.prev = p; \
|
||||
break; \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
/* Unlock an element previously locked by MT_LIST_LOCK_ELT. "np" is the
|
||||
* struct mt_list returned by MT_LIST_LOCK_ELT().
|
||||
*/
|
||||
#define MT_LIST_UNLOCK_ELT(_el, np) \
|
||||
do { \
|
||||
struct mt_list *n = (np).next, *p = (np).prev; \
|
||||
struct mt_list *el = (_el); \
|
||||
(el)->next = n; \
|
||||
(el)->prev = p; \
|
||||
if (n != (el)) \
|
||||
n->prev = (el); \
|
||||
if (p != (el)) \
|
||||
p->next = (el); \
|
||||
} while (0)
|
||||
|
||||
/* Internal macroes for the foreach macroes */
|
||||
#define _MT_LIST_UNLOCK_NEXT(el, np) \
|
||||
do { \
|
||||
struct mt_list *n = (np); \
|
||||
(el)->next = n; \
|
||||
if (n != (el)) \
|
||||
n->prev = (el); \
|
||||
} while (0)
|
||||
|
||||
/* Internal macroes for the foreach macroes */
|
||||
#define _MT_LIST_UNLOCK_PREV(el, np) \
|
||||
do { \
|
||||
struct mt_list *p = (np); \
|
||||
(el)->prev = p; \
|
||||
if (p != (el)) \
|
||||
p->next = (el); \
|
||||
} while (0)
|
||||
|
||||
#define _MT_LIST_LOCK_NEXT(el) \
|
||||
({ \
|
||||
struct mt_list *n = NULL; \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *n2; \
|
||||
n = _HA_ATOMIC_XCHG(&((el)->next), MT_LIST_BUSY); \
|
||||
if (n == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
if (n != (el)) { \
|
||||
n2 = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY);\
|
||||
if (n2 == MT_LIST_BUSY) { \
|
||||
(el)->next = n; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
n; \
|
||||
})
|
||||
|
||||
#define _MT_LIST_LOCK_PREV(el) \
|
||||
({ \
|
||||
struct mt_list *p = NULL; \
|
||||
for (;;__ha_cpu_relax()) { \
|
||||
struct mt_list *p2; \
|
||||
p = _HA_ATOMIC_XCHG(&((el)->prev), MT_LIST_BUSY); \
|
||||
if (p == MT_LIST_BUSY) \
|
||||
continue; \
|
||||
if (p != (el)) { \
|
||||
p2 = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY);\
|
||||
if (p2 == MT_LIST_BUSY) { \
|
||||
(el)->prev = p; \
|
||||
__ha_barrier_store(); \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
p; \
|
||||
})
|
||||
|
||||
#define _MT_LIST_RELINK_DELETED(elt2) \
|
||||
do { \
|
||||
struct mt_list *n = elt2.next, *p = elt2.prev; \
|
||||
ALREADY_CHECKED(p); \
|
||||
n->prev = p; \
|
||||
p->next = n; \
|
||||
} while (0);
|
||||
|
||||
/* Equivalent of MT_LIST_DELETE(), to be used when parsing the list with mt_list_entry_for_each_safe().
|
||||
* It should be the element currently parsed (tmpelt1)
|
||||
*/
|
||||
#define MT_LIST_DELETE_SAFE(_el) \
|
||||
do { \
|
||||
struct mt_list *el = (_el); \
|
||||
(el)->prev = (el); \
|
||||
(el)->next = (el); \
|
||||
(_el) = NULL; \
|
||||
} while (0)
|
||||
|
||||
/* Safe as MT_LIST_DELETE_SAFE, but it won't reinit the element */
|
||||
#define MT_LIST_DELETE_SAFE_NOINIT(_el) \
|
||||
do { \
|
||||
(_el) = NULL; \
|
||||
} while (0)
|
||||
|
||||
/* 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.
|
||||
* tmpelt1 is a temporary struct mt_list *, and tmpelt2 is a temporary
|
||||
* struct mt_list, used internally, both are needed for MT_LIST_DELETE_SAFE.
|
||||
* Example: list_for_each_entry_safe(cur_acl, tmp, known_acl, list, elt1, elt2)
|
||||
* { ... };
|
||||
* If you want to remove the current element, please use MT_LIST_DELETE_SAFE.
|
||||
*/
|
||||
#define mt_list_for_each_entry_safe(item, list_head, member, tmpelt, tmpelt2) \
|
||||
for ((tmpelt) = NULL; (tmpelt) != MT_LIST_BUSY; ({ \
|
||||
if (tmpelt) { \
|
||||
if (tmpelt2.prev) \
|
||||
MT_LIST_UNLOCK_ELT(tmpelt, tmpelt2); \
|
||||
else \
|
||||
_MT_LIST_UNLOCK_NEXT(tmpelt, tmpelt2.next); \
|
||||
} else \
|
||||
_MT_LIST_RELINK_DELETED(tmpelt2); \
|
||||
(tmpelt) = MT_LIST_BUSY; \
|
||||
})) \
|
||||
for ((tmpelt) = (list_head), (tmpelt2).prev = NULL, (tmpelt2).next = _MT_LIST_LOCK_NEXT(tmpelt); ({ \
|
||||
(item) = MT_LIST_ELEM((tmpelt2.next), typeof(item), member); \
|
||||
if (&item->member != (list_head)) { \
|
||||
if (tmpelt2.prev != &item->member) \
|
||||
tmpelt2.next = _MT_LIST_LOCK_NEXT(&item->member); \
|
||||
else \
|
||||
tmpelt2.next = tmpelt; \
|
||||
if (tmpelt != NULL) { \
|
||||
if (tmpelt2.prev) \
|
||||
_MT_LIST_UNLOCK_PREV(tmpelt, tmpelt2.prev); \
|
||||
tmpelt2.prev = tmpelt; \
|
||||
} \
|
||||
(tmpelt) = &item->member; \
|
||||
} \
|
||||
}), \
|
||||
&item->member != (list_head);)
|
||||
|
||||
static __inline struct list *mt_list_to_list(struct mt_list *list)
|
||||
{
|
||||
union {
|
||||
struct mt_list *mt_list;
|
||||
struct list *list;
|
||||
} mylist;
|
||||
|
||||
mylist.mt_list = list;
|
||||
return mylist.list;
|
||||
}
|
||||
|
||||
static __inline struct mt_list *list_to_mt_list(struct list *list)
|
||||
{
|
||||
union {
|
||||
struct mt_list *mt_list;
|
||||
struct list *list;
|
||||
} mylist;
|
||||
|
||||
mylist.list = list;
|
||||
return mylist.mt_list;
|
||||
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_LIST_H */
|
@ -1,309 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/sample-t.h
|
||||
* Macros, variables and structures for sample management.
|
||||
*
|
||||
* Copyright (C) 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
|
||||
* Copyright (C) 2012-2013 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* 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 _HAPROXY_SAMPLE_T_H
|
||||
#define _HAPROXY_SAMPLE_T_H
|
||||
|
||||
#include <haproxy/api-t.h>
|
||||
#include <haproxy/sample_data-t.h>
|
||||
|
||||
/* input and output sample types */
|
||||
enum {
|
||||
SMP_T_ANY = 0, /* any type */
|
||||
SMP_T_BOOL, /* boolean */
|
||||
SMP_T_SINT, /* signed 64bits integer type */
|
||||
SMP_T_ADDR, /* ipv4 or ipv6, only used for input type compatibility */
|
||||
SMP_T_IPV4, /* ipv4 type */
|
||||
SMP_T_IPV6, /* ipv6 type */
|
||||
SMP_T_STR, /* char string type */
|
||||
SMP_T_BIN, /* buffer type */
|
||||
SMP_T_METH, /* contain method */
|
||||
SMP_TYPES /* number of types, must always be last */
|
||||
};
|
||||
|
||||
/* Sample sources are used to establish a relation between fetch keywords and
|
||||
* the location where they're about to be used. They're reserved for internal
|
||||
* use and are not meant to be known outside the sample management code.
|
||||
*/
|
||||
enum {
|
||||
SMP_SRC_CONST, /* constat elements known at configuration time */
|
||||
SMP_SRC_INTRN, /* internal context-less information */
|
||||
SMP_SRC_LISTN, /* listener which accepted the connection */
|
||||
SMP_SRC_FTEND, /* frontend which accepted the connection */
|
||||
SMP_SRC_L4CLI, /* L4 information about the client */
|
||||
SMP_SRC_L5CLI, /* fetch uses client information from embryonic session */
|
||||
SMP_SRC_TRACK, /* fetch involves track counters */
|
||||
SMP_SRC_L6REQ, /* fetch uses raw information from the request buffer */
|
||||
SMP_SRC_HRQHV, /* fetch uses volatile information about HTTP request headers (eg: value) */
|
||||
SMP_SRC_HRQHP, /* fetch uses persistent information about HTTP request headers (eg: meth) */
|
||||
SMP_SRC_HRQBO, /* fetch uses information about HTTP request body */
|
||||
SMP_SRC_BKEND, /* fetch uses information about the backend */
|
||||
SMP_SRC_SERVR, /* fetch uses information about the selected server */
|
||||
SMP_SRC_L4SRV, /* fetch uses information about the server L4 connection */
|
||||
SMP_SRC_L5SRV, /* fetch uses information about the server L5 connection */
|
||||
SMP_SRC_L6RES, /* fetch uses raw information from the response buffer */
|
||||
SMP_SRC_HRSHV, /* fetch uses volatile information about HTTP response headers (eg: value) */
|
||||
SMP_SRC_HRSHP, /* fetch uses persistent information about HTTP response headers (eg: status) */
|
||||
SMP_SRC_HRSBO, /* fetch uses information about HTTP response body */
|
||||
SMP_SRC_RQFIN, /* final information about request buffer (eg: tot bytes) */
|
||||
SMP_SRC_RSFIN, /* final information about response buffer (eg: tot bytes) */
|
||||
SMP_SRC_TXFIN, /* final information about the transaction (eg: #comp rate) */
|
||||
SMP_SRC_SSFIN, /* final information about the stream (eg: #requests, final flags) */
|
||||
SMP_SRC_ENTRIES /* nothing after this */
|
||||
};
|
||||
|
||||
/* Sample checkpoints are a list of places where samples may be used. This is
|
||||
* an internal enum used only to build SMP_VAL_*.
|
||||
*/
|
||||
enum {
|
||||
SMP_CKP_FE_CON_ACC, /* FE connection accept rules ("tcp request connection") */
|
||||
SMP_CKP_FE_SES_ACC, /* FE stream accept rules (to come soon) */
|
||||
SMP_CKP_FE_REQ_CNT, /* FE request content rules ("tcp request content") */
|
||||
SMP_CKP_FE_HRQ_HDR, /* FE HTTP request headers (rules, headers, monitor, stats, redirect) */
|
||||
SMP_CKP_FE_HRQ_BDY, /* FE HTTP request body */
|
||||
SMP_CKP_FE_SET_BCK, /* FE backend switching rules ("use_backend") */
|
||||
SMP_CKP_BE_REQ_CNT, /* BE request content rules ("tcp request content") */
|
||||
SMP_CKP_BE_HRQ_HDR, /* BE HTTP request headers (rules, headers, monitor, stats, redirect) */
|
||||
SMP_CKP_BE_HRQ_BDY, /* BE HTTP request body */
|
||||
SMP_CKP_BE_SET_SRV, /* BE server switching rules ("use_server", "balance", "force-persist", "stick", ...) */
|
||||
SMP_CKP_BE_SRV_CON, /* BE server connect (eg: "source") */
|
||||
SMP_CKP_BE_RES_CNT, /* BE response content rules ("tcp response content") */
|
||||
SMP_CKP_BE_HRS_HDR, /* BE HTTP response headers (rules, headers) */
|
||||
SMP_CKP_BE_HRS_BDY, /* BE HTTP response body (stick-store rules are there) */
|
||||
SMP_CKP_BE_STO_RUL, /* BE stick-store rules */
|
||||
SMP_CKP_FE_RES_CNT, /* FE response content rules ("tcp response content") */
|
||||
SMP_CKP_FE_HRS_HDR, /* FE HTTP response headers (rules, headers) */
|
||||
SMP_CKP_FE_HRS_BDY, /* FE HTTP response body */
|
||||
SMP_CKP_FE_LOG_END, /* FE log at the end of the txn/stream */
|
||||
SMP_CKP_BE_CHK_RUL, /* BE tcp-check rules */
|
||||
SMP_CKP_CFG_PARSER, /* config parser (i.e. before boot) */
|
||||
SMP_CKP_CLI_PARSER, /* command line parser */
|
||||
SMP_CKP_ENTRIES /* nothing after this */
|
||||
};
|
||||
|
||||
/* SMP_USE_* are flags used to declare fetch keywords. Fetch methods are
|
||||
* associated with bitfields composed of these values, generally only one, to
|
||||
* indicate where the contents may be sampled. Some fetches are ambiguous as
|
||||
* they apply to either the request or the response depending on the context,
|
||||
* so they will have 2 of these bits (eg: hdr(), payload(), ...). These are
|
||||
* stored in smp->use.
|
||||
*/
|
||||
enum {
|
||||
SMP_USE_CONST = 1 << SMP_SRC_CONST, /* constant values known at config time */
|
||||
SMP_USE_INTRN = 1 << SMP_SRC_INTRN, /* internal context-less information */
|
||||
SMP_USE_LISTN = 1 << SMP_SRC_LISTN, /* listener which accepted the connection */
|
||||
SMP_USE_FTEND = 1 << SMP_SRC_FTEND, /* frontend which accepted the connection */
|
||||
SMP_USE_L4CLI = 1 << SMP_SRC_L4CLI, /* L4 information about the client */
|
||||
SMP_USE_L5CLI = 1 << SMP_SRC_L5CLI, /* fetch uses client information from embryonic session */
|
||||
SMP_USE_TRACK = 1 << SMP_SRC_TRACK, /* fetch involves track counters */
|
||||
SMP_USE_L6REQ = 1 << SMP_SRC_L6REQ, /* fetch uses raw information from the request buffer */
|
||||
SMP_USE_HRQHV = 1 << SMP_SRC_HRQHV, /* fetch uses volatile information about HTTP request headers (eg: value) */
|
||||
SMP_USE_HRQHP = 1 << SMP_SRC_HRQHP, /* fetch uses persistent information about HTTP request headers (eg: meth) */
|
||||
SMP_USE_HRQBO = 1 << SMP_SRC_HRQBO, /* fetch uses information about HTTP request body */
|
||||
SMP_USE_BKEND = 1 << SMP_SRC_BKEND, /* fetch uses information about the backend */
|
||||
SMP_USE_SERVR = 1 << SMP_SRC_SERVR, /* fetch uses information about the selected server */
|
||||
SMP_USE_L4SRV = 1 << SMP_SRC_L4SRV, /* fetch uses information about the server L4 connection */
|
||||
SMP_USE_L5SRV = 1 << SMP_SRC_L5SRV, /* fetch uses information about the server L5 connection */
|
||||
SMP_USE_L6RES = 1 << SMP_SRC_L6RES, /* fetch uses raw information from the response buffer */
|
||||
SMP_USE_HRSHV = 1 << SMP_SRC_HRSHV, /* fetch uses volatile information about HTTP response headers (eg: value) */
|
||||
SMP_USE_HRSHP = 1 << SMP_SRC_HRSHP, /* fetch uses persistent information about HTTP response headers (eg: status) */
|
||||
SMP_USE_HRSBO = 1 << SMP_SRC_HRSBO, /* fetch uses information about HTTP response body */
|
||||
SMP_USE_RQFIN = 1 << SMP_SRC_RQFIN, /* final information about request buffer (eg: tot bytes) */
|
||||
SMP_USE_RSFIN = 1 << SMP_SRC_RSFIN, /* final information about response buffer (eg: tot bytes) */
|
||||
SMP_USE_TXFIN = 1 << SMP_SRC_TXFIN, /* final information about the transaction (eg: #comp rate) */
|
||||
SMP_USE_SSFIN = 1 << SMP_SRC_SSFIN, /* final information about the stream (eg: #requests, final flags) */
|
||||
|
||||
/* This composite one is useful to detect if an http_txn needs to be allocated */
|
||||
SMP_USE_HTTP_ANY = SMP_USE_HRQHV | SMP_USE_HRQHP | SMP_USE_HRQBO |
|
||||
SMP_USE_HRSHV | SMP_USE_HRSHP | SMP_USE_HRSBO,
|
||||
};
|
||||
|
||||
/* Sample validity is computed from the fetch sources above when keywords
|
||||
* are registered. Each fetch method may be used at different locations. The
|
||||
* configuration parser will check whether the fetches are compatible with the
|
||||
* location where they're used. These are stored in smp->val.
|
||||
*/
|
||||
enum {
|
||||
SMP_VAL___________ = 0, /* Just used as a visual marker */
|
||||
SMP_VAL_FE_CON_ACC = 1 << SMP_CKP_FE_CON_ACC, /* FE connection accept rules ("tcp request connection") */
|
||||
SMP_VAL_FE_SES_ACC = 1 << SMP_CKP_FE_SES_ACC, /* FE stream accept rules (to come soon) */
|
||||
SMP_VAL_FE_REQ_CNT = 1 << SMP_CKP_FE_REQ_CNT, /* FE request content rules ("tcp request content") */
|
||||
SMP_VAL_FE_HRQ_HDR = 1 << SMP_CKP_FE_HRQ_HDR, /* FE HTTP request headers (rules, headers, monitor, stats, redirect) */
|
||||
SMP_VAL_FE_HRQ_BDY = 1 << SMP_CKP_FE_HRQ_BDY, /* FE HTTP request body */
|
||||
SMP_VAL_FE_SET_BCK = 1 << SMP_CKP_FE_SET_BCK, /* FE backend switching rules ("use_backend") */
|
||||
SMP_VAL_BE_REQ_CNT = 1 << SMP_CKP_BE_REQ_CNT, /* BE request content rules ("tcp request content") */
|
||||
SMP_VAL_BE_HRQ_HDR = 1 << SMP_CKP_BE_HRQ_HDR, /* BE HTTP request headers (rules, headers, monitor, stats, redirect) */
|
||||
SMP_VAL_BE_HRQ_BDY = 1 << SMP_CKP_BE_HRQ_BDY, /* BE HTTP request body */
|
||||
SMP_VAL_BE_SET_SRV = 1 << SMP_CKP_BE_SET_SRV, /* BE server switching rules ("use_server", "balance", "force-persist", "stick", ...) */
|
||||
SMP_VAL_BE_SRV_CON = 1 << SMP_CKP_BE_SRV_CON, /* BE server connect (eg: "source") */
|
||||
SMP_VAL_BE_RES_CNT = 1 << SMP_CKP_BE_RES_CNT, /* BE response content rules ("tcp response content") */
|
||||
SMP_VAL_BE_HRS_HDR = 1 << SMP_CKP_BE_HRS_HDR, /* BE HTTP response headers (rules, headers) */
|
||||
SMP_VAL_BE_HRS_BDY = 1 << SMP_CKP_BE_HRS_BDY, /* BE HTTP response body (stick-store rules are there) */
|
||||
SMP_VAL_BE_STO_RUL = 1 << SMP_CKP_BE_STO_RUL, /* BE stick-store rules */
|
||||
SMP_VAL_FE_RES_CNT = 1 << SMP_CKP_FE_RES_CNT, /* FE response content rules ("tcp response content") */
|
||||
SMP_VAL_FE_HRS_HDR = 1 << SMP_CKP_FE_HRS_HDR, /* FE HTTP response headers (rules, headers) */
|
||||
SMP_VAL_FE_HRS_BDY = 1 << SMP_CKP_FE_HRS_BDY, /* FE HTTP response body */
|
||||
SMP_VAL_FE_LOG_END = 1 << SMP_CKP_FE_LOG_END, /* FE log at the end of the txn/stream */
|
||||
SMP_VAL_BE_CHK_RUL = 1 << SMP_CKP_BE_CHK_RUL, /* BE tcp-check rule */
|
||||
SMP_VAL_CFG_PARSER = 1 << SMP_CKP_CFG_PARSER, /* within config parser */
|
||||
SMP_VAL_CLI_PARSER = 1 << SMP_CKP_CLI_PARSER, /* within command line parser */
|
||||
|
||||
/* a few combinations to decide what direction to try to fetch (useful for logs) */
|
||||
SMP_VAL_REQUEST = SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
|
||||
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
|
||||
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
|
||||
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_CHK_RUL,
|
||||
|
||||
SMP_VAL_RESPONSE = SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | SMP_VAL_BE_HRS_HDR |
|
||||
SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | SMP_VAL_FE_RES_CNT |
|
||||
SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | SMP_VAL_FE_LOG_END |
|
||||
SMP_VAL_BE_CHK_RUL,
|
||||
};
|
||||
|
||||
/* Sample fetch options are passed to sample fetch functions to add precision
|
||||
* about what is desired :
|
||||
* - fetch direction (req/resp)
|
||||
* - intermediary / final fetch
|
||||
*/
|
||||
enum {
|
||||
SMP_OPT_DIR_REQ = 0, /* direction = request */
|
||||
SMP_OPT_DIR_RES = 1, /* direction = response */
|
||||
SMP_OPT_DIR = (SMP_OPT_DIR_REQ|SMP_OPT_DIR_RES), /* mask to get direction */
|
||||
SMP_OPT_FINAL = 2, /* final fetch, contents won't change anymore */
|
||||
SMP_OPT_ITERATE = 4, /* fetches may be iterated if supported (for ACLs) */
|
||||
};
|
||||
|
||||
/* Flags used to describe fetched samples. MAY_CHANGE indicates that the result
|
||||
* of the fetch might still evolve, for instance because of more data expected,
|
||||
* even if the fetch has failed. VOL_* indicates how long a result may be cached.
|
||||
*/
|
||||
enum {
|
||||
SMP_F_NOT_LAST = 1 << 0, /* other occurrences might exist for this sample */
|
||||
SMP_F_MAY_CHANGE = 1 << 1, /* sample is unstable and might change (eg: request length) */
|
||||
SMP_F_VOL_TEST = 1 << 2, /* result must not survive longer than the test (eg: time) */
|
||||
SMP_F_VOL_1ST = 1 << 3, /* result sensitive to changes in first line (eg: URI) */
|
||||
SMP_F_VOL_HDR = 1 << 4, /* result sensitive to changes in headers */
|
||||
SMP_F_VOL_TXN = 1 << 5, /* result sensitive to new transaction (eg: HTTP version) */
|
||||
SMP_F_VOL_SESS = 1 << 6, /* result sensitive to new session (eg: src IP) */
|
||||
SMP_F_VOLATILE = (1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6), /* any volatility condition */
|
||||
SMP_F_CONST = 1 << 7, /* This sample use constant memory. May diplicate it before changes */
|
||||
};
|
||||
|
||||
/* needed below */
|
||||
struct session;
|
||||
struct stream;
|
||||
struct arg;
|
||||
|
||||
/* a sample context might be used by any sample fetch function in order to
|
||||
* store information needed across multiple calls (eg: restart point for a
|
||||
* next occurrence). By definition it may store up to 8 pointers, or any
|
||||
* scalar (double, int, long long).
|
||||
*/
|
||||
union smp_ctx {
|
||||
void *p; /* any pointer */
|
||||
int i; /* any integer */
|
||||
long long ll; /* any long long or smaller */
|
||||
double d; /* any float or double */
|
||||
void *a[8]; /* any array of up to 8 pointers */
|
||||
};
|
||||
|
||||
/* a sample is a typed data extracted from a stream. It has a type, contents,
|
||||
* validity constraints, a context for use in iterative calls.
|
||||
*/
|
||||
struct sample {
|
||||
unsigned int flags; /* SMP_F_* */
|
||||
struct sample_data data;
|
||||
union smp_ctx ctx;
|
||||
|
||||
/* Some sample analyzer (sample-fetch or converters) needs to
|
||||
* known the attached proxy, session and stream. The sample-fetches
|
||||
* and the converters function pointers cannot be called without
|
||||
* these 3 pointers filled.
|
||||
*/
|
||||
struct proxy *px;
|
||||
struct session *sess;
|
||||
struct stream *strm; /* WARNING! MAY BE NULL! (eg: tcp-request connection) */
|
||||
unsigned int opt; /* fetch options (SMP_OPT_*) */
|
||||
};
|
||||
|
||||
/* Descriptor for a sample conversion */
|
||||
struct sample_conv {
|
||||
const char *kw; /* configuration keyword */
|
||||
int (*process)(const struct arg *arg_p,
|
||||
struct sample *smp,
|
||||
void *private); /* process function */
|
||||
uint64_t arg_mask; /* arguments (ARG*()) */
|
||||
int (*val_args)(struct arg *arg_p,
|
||||
struct sample_conv *smp_conv,
|
||||
const char *file, int line,
|
||||
char **err_msg); /* argument validation function */
|
||||
unsigned int in_type; /* expected input sample type */
|
||||
unsigned int out_type; /* output sample type */
|
||||
void *private; /* private values. only used by maps and Lua */
|
||||
};
|
||||
|
||||
/* sample conversion expression */
|
||||
struct sample_conv_expr {
|
||||
struct list list; /* member of a sample_expr */
|
||||
struct sample_conv *conv; /* sample conversion used */
|
||||
struct arg *arg_p; /* optional arguments */
|
||||
};
|
||||
|
||||
/* Descriptor for a sample fetch method */
|
||||
struct sample_fetch {
|
||||
const char *kw; /* configuration keyword */
|
||||
int (*process)(const struct arg *arg_p,
|
||||
struct sample *smp,
|
||||
const char *kw, /* fetch processing function */
|
||||
void *private); /* private value. */
|
||||
uint64_t arg_mask; /* arguments (ARG*()) */
|
||||
int (*val_args)(struct arg *arg_p,
|
||||
char **err_msg); /* argument validation function */
|
||||
unsigned long out_type; /* output sample type */
|
||||
unsigned int use; /* fetch source (SMP_USE_*) */
|
||||
unsigned int val; /* fetch validity (SMP_VAL_*) */
|
||||
void *private; /* private values. only used by Lua */
|
||||
};
|
||||
|
||||
/* sample expression */
|
||||
struct sample_expr {
|
||||
struct list list; /* member of list of sample, currently not used */
|
||||
struct sample_fetch *fetch; /* sample fetch method */
|
||||
struct arg *arg_p; /* optional pointer to arguments to fetch function */
|
||||
struct list conv_exprs; /* list of conversion expression to apply */
|
||||
};
|
||||
|
||||
/* sample fetch keywords list */
|
||||
struct sample_fetch_kw_list {
|
||||
struct list list; /* head of sample fetch keyword list */
|
||||
struct sample_fetch kw[VAR_ARRAY]; /* array of sample fetch descriptors */
|
||||
};
|
||||
|
||||
/* sample conversion keywords list */
|
||||
struct sample_conv_kw_list {
|
||||
struct list list; /* head of sample conversion keyword list */
|
||||
struct sample_conv kw[VAR_ARRAY]; /* array of sample conversion descriptors */
|
||||
};
|
||||
|
||||
typedef int (*sample_cast_fct)(struct sample *smp);
|
||||
|
||||
#endif /* _HAPROXY_SAMPLE_T_H */
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/sample_data-t.h
|
||||
* Definitions of sample data
|
||||
*
|
||||
* Copyright (C) 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
|
||||
* Copyright (C) 2020 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* 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 _HAPROXY_SAMPLE_DATA_T_H
|
||||
#define _HAPROXY_SAMPLE_DATA_T_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <haproxy/buf-t.h>
|
||||
#include <haproxy/http-t.h>
|
||||
|
||||
/* Note: the strings below make use of chunks. Chunks may carry an allocated
|
||||
* size in addition to the length. The size counts from the beginning (str)
|
||||
* to the end. If the size is unknown, it MUST be zero, in which case the
|
||||
* sample will automatically be duplicated when a change larger than <len> has
|
||||
* to be performed. Thus it is safe to always set size to zero.
|
||||
*/
|
||||
union sample_value {
|
||||
long long int sint; /* used for signed 64bits integers */
|
||||
struct in_addr ipv4; /* used for ipv4 addresses */
|
||||
struct in6_addr ipv6; /* used for ipv6 addresses */
|
||||
struct buffer str; /* used for char strings or buffers */
|
||||
struct http_meth meth; /* used for http method */
|
||||
};
|
||||
|
||||
/* Used to store sample constant */
|
||||
struct sample_data {
|
||||
int type; /* SMP_T_* */
|
||||
union sample_value u; /* sample data */
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_SAMPLE_DATA_T_H */
|
@ -1,191 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/spoe-t.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 _HAPROXY_SPOE_T_H
|
||||
#define _HAPROXY_SPOE_T_H
|
||||
|
||||
|
||||
/* Type of list of messages */
|
||||
#define SPOE_MSGS_BY_EVENT 0x01
|
||||
#define SPOE_MSGS_BY_GROUP 0x02
|
||||
|
||||
/* 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 */
|
||||
#define SPOE_FL_FORCE_SET_VAR 0x00000020 /* Set when SPOE agent will set all variables from agent (and not only known variables) */
|
||||
|
||||
/* 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_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
|
||||
|
||||
/* 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
|
||||
|
||||
/* All possible states for a SPOE context */
|
||||
enum spoe_ctx_state {
|
||||
SPOE_CTX_ST_NONE = 0,
|
||||
SPOE_CTX_ST_READY,
|
||||
SPOE_CTX_ST_ENCODING_MSGS,
|
||||
SPOE_CTX_ST_SENDING_MSGS,
|
||||
SPOE_CTX_ST_WAITING_ACK,
|
||||
SPOE_CTX_ST_DONE,
|
||||
SPOE_CTX_ST_ERROR,
|
||||
};
|
||||
|
||||
/* All possible states for a SPOE applet */
|
||||
enum spoe_appctx_state {
|
||||
SPOE_APPCTX_ST_CONNECT = 0,
|
||||
SPOE_APPCTX_ST_CONNECTING,
|
||||
SPOE_APPCTX_ST_IDLE,
|
||||
SPOE_APPCTX_ST_PROCESSING,
|
||||
SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY,
|
||||
SPOE_APPCTX_ST_WAITING_SYNC_ACK,
|
||||
SPOE_APPCTX_ST_DISCONNECT,
|
||||
SPOE_APPCTX_ST_DISCONNECTING,
|
||||
SPOE_APPCTX_ST_EXIT,
|
||||
SPOE_APPCTX_ST_END,
|
||||
};
|
||||
|
||||
/* 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_INTERRUPT,
|
||||
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 */
|
||||
};
|
||||
|
||||
/* Frame Types sent by HAProxy and by agents */
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
#endif /* _HAPROXY_SPOE_T_H */
|
@ -1,352 +0,0 @@
|
||||
/*
|
||||
* include/haproxy/spoe.h
|
||||
* Encoding/Decoding functions for the SPOE filters (and other helpers).
|
||||
*
|
||||
* 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 _HAPROXY_SPOE_H
|
||||
#define _HAPROXY_SPOE_H
|
||||
|
||||
#include <string.h>
|
||||
#include <haproxy/api.h>
|
||||
#include <haproxy/intops.h>
|
||||
#include <haproxy/sample-t.h>
|
||||
#include <haproxy/spoe-t.h>
|
||||
|
||||
|
||||
/* 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 <smp>. 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. */
|
||||
static inline int
|
||||
spoe_encode_data(struct sample *smp, char **buf, char *end)
|
||||
{
|
||||
char *p = *buf;
|
||||
int ret;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
if (smp == NULL) {
|
||||
*p++ = SPOE_DATA_T_NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (smp->data.type) {
|
||||
case SMP_T_BOOL:
|
||||
*p = SPOE_DATA_T_BOOL;
|
||||
*p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
|
||||
break;
|
||||
|
||||
case SMP_T_SINT:
|
||||
*p++ = SPOE_DATA_T_INT64;
|
||||
if (encode_varint(smp->data.u.sint, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SMP_T_IPV4:
|
||||
if (p + 5 > end)
|
||||
return -1;
|
||||
*p++ = SPOE_DATA_T_IPV4;
|
||||
memcpy(p, &smp->data.u.ipv4, 4);
|
||||
p += 4;
|
||||
break;
|
||||
|
||||
case SMP_T_IPV6:
|
||||
if (p + 17 > end)
|
||||
return -1;
|
||||
*p++ = SPOE_DATA_T_IPV6;
|
||||
memcpy(p, &smp->data.u.ipv6, 16);
|
||||
p += 16;
|
||||
break;
|
||||
|
||||
case SMP_T_STR:
|
||||
case SMP_T_BIN: {
|
||||
/* If defined, get length and offset of the sample by reading the sample
|
||||
* context. ctx.a[0] is the pointer to the length and ctx.a[1] is the
|
||||
* pointer to the offset. If the offset is greater than 0, it means the
|
||||
* sample is partially encoded. In this case, we only need to encode the
|
||||
* reamining. When all the sample is encoded, the offset is reset to 0.
|
||||
* So the caller know it can try to encode the next sample. */
|
||||
struct buffer *chk = &smp->data.u.str;
|
||||
unsigned int *len = smp->ctx.a[0];
|
||||
unsigned int *off = smp->ctx.a[1];
|
||||
|
||||
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. */
|
||||
struct buffer *chk = &smp->data.u.str;
|
||||
|
||||
*p++ = (smp->data.type == SMP_T_STR)
|
||||
? SPOE_DATA_T_STR
|
||||
: SPOE_DATA_T_BIN;
|
||||
ret = spoe_encode_frag_buffer(chk->area,
|
||||
chk->data, &p,
|
||||
end);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
*len = chk->data;
|
||||
}
|
||||
else {
|
||||
/* The sample has been fragmented, encode remaining data */
|
||||
ret = MIN(*len - *off, end - p);
|
||||
memcpy(p, chk->area + *off, ret);
|
||||
p += ret;
|
||||
}
|
||||
/* Now update <*off> */
|
||||
if (ret + *off != *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.area;
|
||||
len = smp->data.u.meth.str.data;
|
||||
}
|
||||
if (spoe_encode_buffer(m, len, &p, end) == -1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
*p++ = 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, struct sample *smp)
|
||||
{
|
||||
char *str, *p = *buf;
|
||||
int type, r = 0;
|
||||
uint64_t sz;
|
||||
|
||||
if (p >= end)
|
||||
return -1;
|
||||
|
||||
type = *p++;
|
||||
switch (type & SPOE_DATA_T_MASK) {
|
||||
case SPOE_DATA_T_BOOL:
|
||||
smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE);
|
||||
smp->data.type = SMP_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, (uint64_t *)&smp->data.u.sint) == -1)
|
||||
return -1;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV4:
|
||||
if (p+4 > end)
|
||||
return -1;
|
||||
smp->data.type = SMP_T_IPV4;
|
||||
memcpy(&smp->data.u.ipv4, p, 4);
|
||||
p += 4;
|
||||
break;
|
||||
case SPOE_DATA_T_IPV6:
|
||||
if (p+16 > end)
|
||||
return -1;
|
||||
memcpy(&smp->data.u.ipv6, p, 16);
|
||||
smp->data.type = SMP_T_IPV6;
|
||||
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;
|
||||
smp->data.u.str.area = str;
|
||||
smp->data.u.str.data = sz;
|
||||
smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
r = (p - *buf);
|
||||
*buf = p;
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_SPOE_H */
|
@ -1,636 +0,0 @@
|
||||
/*
|
||||
* Modsecurity wrapper for haproxy
|
||||
*
|
||||
* This file contains the wrapper which sends data in ModSecurity
|
||||
* and returns the verdict.
|
||||
*
|
||||
* Copyright 2016 OZON, Thierry Fournier <thierry.fournier@ozon.io>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <haproxy/intops.h>
|
||||
#include <haproxy/sample-t.h>
|
||||
|
||||
#include <api.h>
|
||||
|
||||
#include "modsec_wrapper.h"
|
||||
#include "spoa.h"
|
||||
|
||||
static char host_name[60];
|
||||
|
||||
/* Note: The document and the code of "apr_table_make" considers
|
||||
* that this function doesn't fails. The Apache APR code says
|
||||
* other thing. If the system doesn't have any more memory, a
|
||||
* a segfault occurs :(. Be carrefull with this module.
|
||||
*/
|
||||
|
||||
struct directory_config *modsec_config = NULL;
|
||||
static server_rec *modsec_server = NULL;
|
||||
|
||||
struct apr_bucket_haproxy {
|
||||
apr_bucket_refcount refcount;
|
||||
char *buffer;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
static void haproxy_bucket_destroy(void *data)
|
||||
{
|
||||
struct apr_bucket_haproxy *bucket = data;
|
||||
|
||||
if (apr_bucket_shared_destroy(bucket))
|
||||
apr_bucket_free(bucket);
|
||||
}
|
||||
|
||||
static apr_status_t haproxy_bucket_read(apr_bucket *bucket, const char **str,
|
||||
apr_size_t *len, apr_read_type_e block)
|
||||
{
|
||||
struct apr_bucket_haproxy *data = bucket->data;
|
||||
|
||||
if (bucket->start) {
|
||||
*str = NULL;
|
||||
*len = 0;
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
*str = data->buffer;
|
||||
*len = data->length;
|
||||
bucket->start = 1; /* Just a flag to say that the read is started */
|
||||
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
static const apr_bucket_type_t apr_bucket_type_haproxy = {
|
||||
"HAProxy", 7, APR_BUCKET_DATA,
|
||||
haproxy_bucket_destroy,
|
||||
haproxy_bucket_read,
|
||||
apr_bucket_setaside_noop,
|
||||
apr_bucket_shared_split,
|
||||
apr_bucket_shared_copy
|
||||
};
|
||||
|
||||
static char *chunk_strdup(struct request_rec *req, const char *str, size_t len)
|
||||
{
|
||||
char *out;
|
||||
|
||||
out = apr_pcalloc(req->pool, len + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
memcpy(out, str, len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static char *printf_dup(struct request_rec *req, char *fmt, ...)
|
||||
{
|
||||
char *out;
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(NULL, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len == -1)
|
||||
return NULL;
|
||||
|
||||
out = apr_pcalloc(req->pool, len + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(out, len + 1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len == -1)
|
||||
return NULL;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* This function send logs. For now, it do nothing. */
|
||||
static void modsec_log(void *obj, int level, char *str)
|
||||
{
|
||||
LOG(&null_worker, "%s", str);
|
||||
}
|
||||
|
||||
/* This function load the ModSecurity file. It returns -1 if the
|
||||
* initialisation fails.
|
||||
*/
|
||||
int modsecurity_load(const char *file)
|
||||
{
|
||||
const char *msg;
|
||||
char cwd[128];
|
||||
|
||||
/* Initialises modsecurity. */
|
||||
|
||||
modsec_server = modsecInit();
|
||||
if (modsec_server == NULL) {
|
||||
LOG(&null_worker, "ModSecurity initialisation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
modsecSetLogHook(NULL, modsec_log);
|
||||
|
||||
gethostname(host_name, 60);
|
||||
modsec_server->server_hostname = host_name;
|
||||
|
||||
modsecStartConfig();
|
||||
|
||||
modsec_config = modsecGetDefaultConfig();
|
||||
if (modsec_config == NULL) {
|
||||
LOG(&null_worker, "ModSecurity default configuration initialisation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = modsecProcessConfig(modsec_config, file, getcwd(cwd, 128));
|
||||
if (msg != NULL) {
|
||||
LOG(&null_worker, "ModSecurity load configuration failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
modsecFinalizeConfig();
|
||||
|
||||
modsecInitProcess();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct modsec_hdr {
|
||||
const char *name;
|
||||
uint64_t name_len;
|
||||
const char *value;
|
||||
uint64_t value_len;
|
||||
};
|
||||
|
||||
int modsecurity_process(struct worker *worker, struct modsecurity_parameters *params)
|
||||
{
|
||||
struct conn_rec *cr;
|
||||
struct request_rec *req;
|
||||
struct apr_bucket_brigade *brigade;
|
||||
struct apr_bucket *link_bucket;
|
||||
struct apr_bucket_haproxy *data_bucket;
|
||||
struct apr_bucket *last_bucket;
|
||||
int i;
|
||||
long clength;
|
||||
char *err;
|
||||
int fail;
|
||||
const char *lang;
|
||||
char *name, *value;
|
||||
// int body_partial;
|
||||
struct timeval now;
|
||||
int ret;
|
||||
char *buf;
|
||||
char *end;
|
||||
const char *uniqueid;
|
||||
uint64_t uniqueid_len;
|
||||
const char *meth;
|
||||
uint64_t meth_len;
|
||||
const char *path;
|
||||
uint64_t path_len;
|
||||
const char *qs;
|
||||
uint64_t qs_len;
|
||||
const char *vers;
|
||||
uint64_t vers_len;
|
||||
const char *body;
|
||||
uint64_t body_len;
|
||||
uint64_t body_exposed_len;
|
||||
uint64_t hdr_nb;
|
||||
struct modsec_hdr hdrs[255];
|
||||
struct modsec_hdr hdr;
|
||||
int status;
|
||||
int return_code = -1;
|
||||
|
||||
/* Decode uniqueid. */
|
||||
uniqueid = params->uniqueid.data.u.str.area;
|
||||
uniqueid_len = params->uniqueid.data.u.str.data;
|
||||
|
||||
/* Decode method. */
|
||||
meth = params->method.data.u.str.area;
|
||||
meth_len = params->method.data.u.str.data;
|
||||
|
||||
/* Decode path. */
|
||||
path = params->path.data.u.str.area;
|
||||
path_len = params->path.data.u.str.data;
|
||||
|
||||
/* Decode query string. */
|
||||
qs = params->query.data.u.str.area;
|
||||
qs_len = params->query.data.u.str.data;
|
||||
|
||||
/* Decode version. */
|
||||
vers = params->vers.data.u.str.area;
|
||||
vers_len = params->vers.data.u.str.data;
|
||||
|
||||
/* Decode header binary block. */
|
||||
buf = params->hdrs_bin.data.u.str.area;
|
||||
end = buf + params->hdrs_bin.data.u.str.data;
|
||||
|
||||
/* Decode each header. */
|
||||
hdr_nb = 0;
|
||||
while (1) {
|
||||
|
||||
/* Initialise the storage struct. It is useless
|
||||
* because the process fail if the struct is not
|
||||
* fully filled. This init is just does in order
|
||||
* to prevent bug after some improvements.
|
||||
*/
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
/* Decode header name. */
|
||||
ret = decode_varint(&buf, end, &hdr.name_len);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
hdr.name = buf;
|
||||
buf += hdr.name_len;
|
||||
if (buf > end)
|
||||
return -1;
|
||||
|
||||
/* Decode header value. */
|
||||
ret = decode_varint(&buf, end, &hdr.value_len);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
hdr.value = buf;
|
||||
buf += hdr.value_len;
|
||||
if (buf > end)
|
||||
return -1;
|
||||
|
||||
/* Detect the end of the headers. */
|
||||
if (hdr.name_len == 0 && hdr.value_len == 0)
|
||||
break;
|
||||
|
||||
/* Store the header. */
|
||||
if (hdr_nb < 255) {
|
||||
memcpy(&hdrs[hdr_nb], &hdr, sizeof(hdr));
|
||||
hdr_nb++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode body length. Note that the following control
|
||||
* is just set for avoifing a gcc warning.
|
||||
*/
|
||||
body_exposed_len = (uint64_t)params->body_length.data.u.sint;
|
||||
if (body_exposed_len < 0)
|
||||
return -1;
|
||||
|
||||
/* Decode body. */
|
||||
body = params->body.data.u.str.area;
|
||||
body_len = params->body.data.u.str.data;
|
||||
|
||||
fail = 1;
|
||||
|
||||
/* Init processing */
|
||||
|
||||
cr = modsecNewConnection();
|
||||
req = modsecNewRequest(cr, modsec_config);
|
||||
|
||||
/* Load request. */
|
||||
|
||||
req->proxyreq = PROXYREQ_NONE;
|
||||
req->header_only = 0; /* May modified later */
|
||||
|
||||
/* Copy header list. */
|
||||
|
||||
for (i = 0; i < hdr_nb; i++) {
|
||||
name = chunk_strdup(req, hdrs[i].name, hdrs[i].name_len);
|
||||
if (!name) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
value = chunk_strdup(req, hdrs[i].value, hdrs[i].value_len);
|
||||
if (!value) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
apr_table_setn(req->headers_in, name, value);
|
||||
}
|
||||
|
||||
/* Process special headers. */
|
||||
req->range = apr_table_get(req->headers_in, "Range");
|
||||
req->content_type = apr_table_get(req->headers_in, "Content-Type");
|
||||
req->content_encoding = apr_table_get(req->headers_in, "Content-Encoding");
|
||||
req->hostname = apr_table_get(req->headers_in, "Host");
|
||||
if (req->hostname != NULL) {
|
||||
req->parsed_uri.hostname = chunk_strdup(req, req->hostname, strlen(req->hostname));
|
||||
} else {
|
||||
req->parsed_uri.hostname = NULL;
|
||||
}
|
||||
|
||||
lang = apr_table_get(req->headers_in, "Content-Languages");
|
||||
if (lang != NULL) {
|
||||
req->content_languages = apr_array_make(req->pool, 1, sizeof(const char *));
|
||||
*(const char **)apr_array_push(req->content_languages) = lang;
|
||||
}
|
||||
|
||||
lang = apr_table_get(req->headers_in, "Content-Length");
|
||||
if (lang) {
|
||||
errno = 0;
|
||||
clength = strtol(lang, &err, 10);
|
||||
if (*err != '\0' || errno != 0 || clength < 0 || clength > INT_MAX) {
|
||||
errno = ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
req->clength = clength;
|
||||
}
|
||||
|
||||
/* Copy the first line of the request. */
|
||||
req->the_request = printf_dup(req, "%.*s %.*s%s%.*s %.*s",
|
||||
meth_len, meth,
|
||||
path_len, path,
|
||||
qs_len > 0 ? "?" : "",
|
||||
qs_len, qs,
|
||||
vers_len, vers);
|
||||
if (!req->the_request) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy the method. */
|
||||
req->method = chunk_strdup(req, meth, meth_len);
|
||||
if (!req->method) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set the method number. */
|
||||
if (meth_len < 3) {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Detect the method */
|
||||
switch (meth_len) {
|
||||
case 3:
|
||||
if (strncmp(req->method, "GET", 3) == 0)
|
||||
req->method_number = M_GET;
|
||||
else if (strncmp(req->method, "PUT", 3) == 0)
|
||||
req->method_number = M_PUT;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (strncmp(req->method, "POST", 4) == 0)
|
||||
req->method_number = M_POST;
|
||||
else if (strncmp(req->method, "HEAD", 4) == 0) {
|
||||
req->method_number = M_GET;
|
||||
req->header_only = 1;
|
||||
}
|
||||
else if (strncmp(req->method, "COPY", 4) == 0)
|
||||
req->method_number = M_COPY;
|
||||
else if (strncmp(req->method, "MOVE", 4) == 0)
|
||||
req->method_number = M_MOVE;
|
||||
else if (strncmp(req->method, "LOCK", 4) == 0)
|
||||
req->method_number = M_LOCK;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (strncmp(req->method, "TRACE", 5) == 0)
|
||||
req->method_number = M_TRACE;
|
||||
else if (strncmp(req->method, "PATCH", 5) == 0)
|
||||
req->method_number = M_PATCH;
|
||||
else if (strncmp(req->method, "MKCOL", 5) == 0)
|
||||
req->method_number = M_MKCOL;
|
||||
else if (strncmp(req->method, "MERGE", 5) == 0)
|
||||
req->method_number = M_MERGE;
|
||||
else if (strncmp(req->method, "LABEL", 5) == 0)
|
||||
req->method_number = M_LABEL;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (strncmp(req->method, "DELETE", 6) == 0)
|
||||
req->method_number = M_DELETE;
|
||||
else if (strncmp(req->method, "REPORT", 6) == 0)
|
||||
req->method_number = M_REPORT;
|
||||
else if (strncmp(req->method, "UPDATE", 6) == 0)
|
||||
req->method_number = M_UPDATE;
|
||||
else if (strncmp(req->method, "UNLOCK", 6) == 0)
|
||||
req->method_number = M_UNLOCK;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (strncmp(req->method, "CHECKIN", 7) == 0)
|
||||
req->method_number = M_CHECKIN;
|
||||
else if (strncmp(req->method, "INVALID", 7) == 0)
|
||||
req->method_number = M_INVALID;
|
||||
else if (strncmp(req->method, "CONNECT", 7) == 0)
|
||||
req->method_number = M_CONNECT;
|
||||
else if (strncmp(req->method, "OPTIONS", 7) == 0)
|
||||
req->method_number = M_OPTIONS;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (strncmp(req->method, "PROPFIND", 8) == 0)
|
||||
req->method_number = M_PROPFIND;
|
||||
else if (strncmp(req->method, "CHECKOUT", 8) == 0)
|
||||
req->method_number = M_CHECKOUT;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (strncmp(req->method, "PROPPATCH", 9) == 0)
|
||||
req->method_number = M_PROPPATCH;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (strncmp(req->method, "MKACTIVITY", 10) == 0)
|
||||
req->method_number = M_MKACTIVITY;
|
||||
else if (strncmp(req->method, "UNCHECKOUT", 10) == 0)
|
||||
req->method_number = M_UNCHECKOUT;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
if (strncmp(req->method, "MKWORKSPACE", 11) == 0)
|
||||
req->method_number = M_MKWORKSPACE;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 15:
|
||||
if (strncmp(req->method, "VERSION_CONTROL", 15) == 0)
|
||||
req->method_number = M_VERSION_CONTROL;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (strncmp(req->method, "BASELINE_CONTROL", 16) == 0)
|
||||
req->method_number = M_BASELINE_CONTROL;
|
||||
else {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy the protocol. */
|
||||
req->protocol = chunk_strdup(req, vers, vers_len);
|
||||
if (!req->protocol) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Compute the protocol number. */
|
||||
if (vers_len >= 8)
|
||||
req->proto_num = 1000 + !!(vers[7] == '1');
|
||||
|
||||
/* The request time. */
|
||||
gettimeofday(&now, NULL);
|
||||
req->request_time = apr_time_make(now.tv_sec, now.tv_usec / 1000);
|
||||
|
||||
/* No status line. */
|
||||
req->status_line = NULL;
|
||||
req->status = 0;
|
||||
|
||||
/* Copy path. */
|
||||
req->parsed_uri.path = chunk_strdup(req, path, path_len);
|
||||
if (!req->parsed_uri.path) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy args (query string). */
|
||||
req->args = chunk_strdup(req, qs, qs_len);
|
||||
if (!req->args) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set parsed_uri */
|
||||
|
||||
req->parsed_uri.scheme = "http";
|
||||
|
||||
if (req->hostname && req->parsed_uri.scheme && req->parsed_uri.path) {
|
||||
i = snprintf(NULL, 0, "%s://%s%s",
|
||||
req->parsed_uri.scheme, req->hostname, req->parsed_uri.path);
|
||||
req->uri = apr_pcalloc(req->pool, i + 1);
|
||||
if (!req->uri) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
i = snprintf(req->uri, i + 1, "%s://%s%s",
|
||||
req->parsed_uri.scheme, req->hostname, req->parsed_uri.path);
|
||||
}
|
||||
|
||||
req->filename = req->parsed_uri.path;
|
||||
|
||||
/* Set unique id */
|
||||
|
||||
apr_table_setn(req->subprocess_env, "UNIQUE_ID", chunk_strdup(req, uniqueid, uniqueid_len));
|
||||
|
||||
/*
|
||||
*
|
||||
* Load body.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Create an empty bucket brigade */
|
||||
brigade = apr_brigade_create(req->pool, req->connection->bucket_alloc);
|
||||
if (!brigade) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Stores HTTP body available data in a bucket */
|
||||
data_bucket = apr_bucket_alloc(sizeof(*data_bucket), req->connection->bucket_alloc);
|
||||
if (!data_bucket) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
data_bucket->buffer = (char *)body;
|
||||
data_bucket->length = body_len;
|
||||
|
||||
/* Create linked bucket */
|
||||
link_bucket = apr_bucket_alloc(sizeof(*link_bucket), req->connection->bucket_alloc);
|
||||
if (!link_bucket) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
APR_BUCKET_INIT(link_bucket); /* link */
|
||||
link_bucket->free = apr_bucket_free;
|
||||
link_bucket->list = req->connection->bucket_alloc;
|
||||
link_bucket = apr_bucket_shared_make(link_bucket, data_bucket, 0, body_len);
|
||||
link_bucket->type = &apr_bucket_type_haproxy;
|
||||
|
||||
/* Insert the bucket at the end of the brigade. */
|
||||
APR_BRIGADE_INSERT_TAIL(brigade, link_bucket);
|
||||
|
||||
/* Insert the last bucket. */
|
||||
last_bucket = apr_bucket_eos_create(req->connection->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(brigade, last_bucket);
|
||||
|
||||
/* Declares the bucket brigade in modsecurity */
|
||||
modsecSetBodyBrigade(req, brigade);
|
||||
|
||||
/*
|
||||
*
|
||||
* Process analysis.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Process request headers analysis. */
|
||||
status = modsecProcessRequestHeaders(req);
|
||||
if (status != DECLINED && status != DONE)
|
||||
return_code = status;
|
||||
|
||||
/* Process request body analysis. */
|
||||
status = modsecProcessRequestBody(req);
|
||||
if (status != DECLINED && status != DONE)
|
||||
return_code = status;
|
||||
|
||||
/* End processing. */
|
||||
|
||||
fail = 0;
|
||||
if (return_code == -1)
|
||||
return_code = 0;
|
||||
|
||||
fail:
|
||||
|
||||
modsecFinishRequest(req);
|
||||
modsecFinishConnection(cr);
|
||||
|
||||
if (fail) {
|
||||
|
||||
/* errno == ERANGE / ENOMEM / EINVAL */
|
||||
switch (errno) {
|
||||
case ERANGE: LOG(worker, "Invalid range");
|
||||
case ENOMEM: LOG(worker, "Out of memory error");
|
||||
case EINVAL: LOG(worker, "Invalid value");
|
||||
default: LOG(worker, "Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Modsecurity wrapper for haproxy
|
||||
*
|
||||
* This file contains the headers of the wrapper which sends data
|
||||
* in ModSecurity and returns the verdict.
|
||||
*
|
||||
* Copyright 2016 OZON, Thierry Fournier <thierry.fournier@ozon.io>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef __MODSEC_WRAPPER_H__
|
||||
#define __MODSEC_WRAPPER_H__
|
||||
|
||||
#include "spoa.h"
|
||||
|
||||
struct modsecurity_parameters {
|
||||
struct sample uniqueid;
|
||||
struct sample method;
|
||||
struct sample path;
|
||||
struct sample query;
|
||||
struct sample vers;
|
||||
struct sample hdrs_bin;
|
||||
struct sample body_length;
|
||||
struct sample body;
|
||||
};
|
||||
|
||||
int modsecurity_load(const char *file);
|
||||
int modsecurity_process(struct worker *worker, struct modsecurity_parameters *params);
|
||||
|
||||
#endif /* __MODSEC_WRAPPER_H__ */
|
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Modsecurity wrapper for haproxy
|
||||
*
|
||||
* This file contains the headers of the bootstrap for laucnching and scheduling
|
||||
* modsecurity for working with HAProxy SPOE protocol.
|
||||
*
|
||||
* Copyright 2016 OZON, Thierry Fournier <thierry.fournier@ozon.io>
|
||||
*
|
||||
* This file is inherited from "A Random IP reputation service acting as a Stream
|
||||
* Processing Offload Agent"
|
||||
*
|
||||
* Copyright 2016 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef __SPOA_H__
|
||||
#define __SPOA_H__
|
||||
|
||||
#undef LIST_HEAD
|
||||
|
||||
#include <event2/util.h>
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_struct.h>
|
||||
#include <event2/thread.h>
|
||||
|
||||
struct worker {
|
||||
pthread_t thread;
|
||||
int id;
|
||||
struct event_base *base;
|
||||
struct event *monitor_event;
|
||||
|
||||
struct list engines;
|
||||
|
||||
unsigned int nbclients;
|
||||
struct list clients;
|
||||
|
||||
struct list frames;
|
||||
};
|
||||
|
||||
#define LOG(worker, fmt, args...) \
|
||||
do { \
|
||||
struct timeval now; \
|
||||
\
|
||||
gettimeofday(&now, NULL); \
|
||||
fprintf(stderr, "%ld.%06ld [%02d] " fmt "\n", \
|
||||
now.tv_sec, now.tv_usec, (worker)->id, ##args); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __SPOA_H__ */
|
||||
|
||||
extern struct worker null_worker;
|
Loading…
Reference in New Issue
Block a user