dev/hpack/decode
dev/hpack/gen-rht
contrib/mod_defender/defender
-contrib/modsecurity/modsecurity
/src/dlmalloc.c
/tests/test_hashes
+++ /dev/null
-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 $@ $<
+++ /dev/null
-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.).
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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__ */
+++ /dev/null
-/*
- * Modsecurity wrapper for haproxy
- *
- * This file contains the bootstrap for launching 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.
- *
- */
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <stdio.h>
-#include <signal.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <err.h>
-#include <ctype.h>
-
-#include <pthread.h>
-
-#include <event2/util.h>
-#include <event2/event.h>
-#include <event2/event_struct.h>
-#include <event2/thread.h>
-
-#include <haproxy/list.h>
-#include <haproxy/spoe.h>
-
-#include "spoa.h"
-#include "modsec_wrapper.h"
-
-#define DEFAULT_PORT 12345
-#define CONNECTION_BACKLOG 10
-#define NUM_WORKERS 10
-#define MAX_FRAME_SIZE 16384
-#define SPOP_VERSION "2.0"
-
-#define SLEN(str) (sizeof(str)-1)
-
-#define DEBUG(x...) \
- do { \
- if (debug) \
- LOG(x); \
- } while (0)
-
-
-enum spoa_state {
- SPOA_ST_CONNECTING = 0,
- SPOA_ST_PROCESSING,
- SPOA_ST_DISCONNECTING,
-};
-
-enum spoa_frame_type {
- SPOA_FRM_T_UNKNOWN = 0,
- SPOA_FRM_T_HAPROXY,
- SPOA_FRM_T_AGENT,
-};
-
-struct spoe_engine {
- char *id;
-
- struct list processing_frames;
- struct list outgoing_frames;
-
- struct list clients;
- struct list list;
-};
-
-struct spoe_frame {
- enum spoa_frame_type type;
- char *buf;
- unsigned int offset;
- unsigned int len;
-
- unsigned int stream_id;
- unsigned int frame_id;
- unsigned int flags;
- bool hcheck; /* true is the CONNECT frame is a healthcheck */
- bool fragmented; /* true if the frame is fragmented */
- int modsec_code; /* modsecurity return code. -1 if unset, 0 if none, other it returns http code. */
-
- struct event process_frame_event;
- struct worker *worker;
- struct spoe_engine *engine;
- struct client *client;
- struct list list;
-
- char *frag_buf; /* used to accumulate payload of a fragmented frame */
- unsigned int frag_len;
-
- char data[0];
-};
-
-struct client {
- int fd;
- unsigned long id;
- enum spoa_state state;
-
- struct event read_frame_event;
- struct event write_frame_event;
-
- struct spoe_frame *incoming_frame;
- struct spoe_frame *outgoing_frame;
-
- struct list processing_frames;
- struct list outgoing_frames;
-
- unsigned int max_frame_size;
- int status_code;
-
- char *engine_id;
- struct spoe_engine *engine;
- bool pipelining;
- bool async;
- bool fragmentation;
-
- struct worker *worker;
- struct list by_worker;
- struct list by_engine;
-};
-
-/* Globals */
-static struct worker *workers = NULL;
- struct worker null_worker = { .id = 0 };
-static unsigned long clicount = 0;
-static int server_port = DEFAULT_PORT;
-static int num_workers = NUM_WORKERS;
-static unsigned int max_frame_size = MAX_FRAME_SIZE;
-struct timeval processing_delay = {0, 0};
-static bool debug = false;
-static bool pipelining = false;
-static bool async = false;
-static bool fragmentation = false;
-
-
-static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
- [SPOE_FRM_ERR_NONE] = "normal",
- [SPOE_FRM_ERR_IO] = "I/O error",
- [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
- [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
- [SPOE_FRM_ERR_INVALID] = "invalid frame received",
- [SPOE_FRM_ERR_NO_VSN] = "version value not found",
- [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
- [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
- [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
- [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
- [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
- [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
- [SPOE_FRM_ERR_FRAMEID_NOTFOUND] = "frame-id not found",
- [SPOE_FRM_ERR_RES] = "resource allocation error",
- [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
-};
-
-static void signal_cb(evutil_socket_t, short, void *);
-static void accept_cb(evutil_socket_t, short, void *);
-static void worker_monitor_cb(evutil_socket_t, short, void *);
-static void process_frame_cb(evutil_socket_t, short, void *);
-static void read_frame_cb(evutil_socket_t, short, void *);
-static void write_frame_cb(evutil_socket_t, short, void *);
-
-static void use_spoe_engine(struct client *);
-static void unuse_spoe_engine(struct client *);
-static void release_frame(struct spoe_frame *);
-static void release_client(struct client *);
-
-/* Check the protocol version. It returns -1 if an error occurred, the number of
- * read bytes otherwise. */
-static int
-check_proto_version(struct spoe_frame *frame, char **buf, char *end)
-{
- char *str, *p = *buf;
- uint64_t sz;
- int ret;
-
- /* Get the list of all supported versions by HAProxy */
- if ((*p++ & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR)
- return -1;
- ret = spoe_decode_buffer(&p, end, &str, &sz);
- if (ret == -1 || !str)
- return -1;
-
- DEBUG(frame->worker, "<%lu> Supported versions : %.*s",
- frame->client->id, (int)sz, str);
-
- /* TODO: Find the right version in supported ones */
-
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-/* Check max frame size value. It returns -1 if an error occurred, the number of
- * read bytes otherwise. */
-static int
-check_max_frame_size(struct spoe_frame *frame, char **buf, char *end)
-{
- char *p = *buf;
- uint64_t sz;
- int type, ret;
-
- /* Get the max-frame-size value of HAProxy */
- type = *p++;
- if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64)
- return -1;
- if (decode_varint(&p, end, &sz) == -1)
- return -1;
-
- /* Keep the lower value */
- if (sz < frame->client->max_frame_size)
- frame->client->max_frame_size = sz;
-
- DEBUG(frame->worker, "<%lu> HAProxy maximum frame size : %u",
- frame->client->id, (unsigned int)sz);
-
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-/* Check healthcheck value. It returns -1 if an error occurred, the number of
- * read bytes otherwise. */
-static int
-check_healthcheck(struct spoe_frame *frame, char **buf, char *end)
-{
- char *p = *buf;
- int type, ret;
-
- /* Get the "healthcheck" value */
- type = *p++;
- if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_BOOL)
- return -1;
- frame->hcheck = ((type & SPOE_DATA_FL_TRUE) == SPOE_DATA_FL_TRUE);
-
- DEBUG(frame->worker, "<%lu> HELLO healthcheck : %s",
- frame->client->id, (frame->hcheck ? "true" : "false"));
-
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-/* Check capabilities value. It returns -1 if an error occurred, the number of
- * read bytes otherwise. */
-static int
-check_capabilities(struct spoe_frame *frame, char **buf, char *end)
-{
- struct client *client = frame->client;
- char *str, *p = *buf;
- uint64_t sz;
- int ret;
-
- if ((*p++ & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR)
- return -1;
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- return -1;
- if (str == NULL) /* this is not an error */
- goto end;
-
- DEBUG(frame->worker, "<%lu> HAProxy capabilities : %.*s",
- client->id, (int)sz, str);
-
- while (sz) {
- char *delim;
-
- /* Skip leading spaces */
- for (; isspace(*str) && sz; sz--);
-
- if (sz >= 10 && !strncmp(str, "pipelining", 10)) {
- str += 10; sz -= 10;
- if (!sz || isspace(*str) || *str == ',') {
- DEBUG(frame->worker,
- "<%lu> HAProxy supports frame pipelining",
- client->id);
- client->pipelining = true;
- }
- }
- else if (sz >= 5 && !strncmp(str, "async", 5)) {
- str += 5; sz -= 5;
- if (!sz || isspace(*str) || *str == ',') {
- DEBUG(frame->worker,
- "<%lu> HAProxy supports asynchronous frame",
- client->id);
- client->async = true;
- }
- }
- else if (sz >= 13 && !strncmp(str, "fragmentation", 13)) {
- str += 13; sz -= 13;
- if (!sz || isspace(*str) || *str == ',') {
- DEBUG(frame->worker,
- "<%lu> HAProxy supports fragmented frame",
- client->id);
- client->fragmentation = true;
- }
- }
-
- if (!sz || (delim = memchr(str, ',', sz)) == NULL)
- break;
- delim++;
- sz -= (delim - str);
- str = delim;
- }
- end:
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-/* Check engine-id value. It returns -1 if an error occurred, the number of
- * read bytes otherwise. */
-static int
-check_engine_id(struct spoe_frame *frame, char **buf, char *end)
-{
- struct client *client = frame->client;
- char *str, *p = *buf;
- uint64_t sz;
- int ret;
-
- if ((*p++ & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR)
- return -1;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- return -1;
- if (str == NULL) /* this is not an error */
- goto end;
-
- if (client->engine != NULL)
- goto end;
-
- DEBUG(frame->worker, "<%lu> HAProxy engine id : %.*s",
- client->id, (int)sz, str);
-
- client->engine_id = strndup(str, (int)sz);
- end:
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-static int
-acc_payload(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *buf;
- size_t len = frame->len - frame->offset;
- int ret = frame->offset;
-
- /* No need to accumulation payload */
- if (frame->fragmented == false)
- return ret;
-
- buf = realloc(frame->frag_buf, frame->frag_len + len);
- if (buf == NULL) {
- client->status_code = SPOE_FRM_ERR_RES;
- return -1;
- }
- memcpy(buf + frame->frag_len, frame->buf + frame->offset, len);
- frame->frag_buf = buf;
- frame->frag_len += len;
-
- if (!(frame->flags & SPOE_FRM_FL_FIN)) {
- /* Wait for next parts */
- frame->buf = (char *)(frame->data);
- frame->offset = 0;
- frame->len = 0;
- frame->flags = 0;
- return 1;
- }
-
- frame->buf = frame->frag_buf;
- frame->len = frame->frag_len;
- frame->offset = 0;
- return ret;
-}
-
-/* Check disconnect status code. It returns -1 if an error occurred, the number
- * of read bytes otherwise. */
-static int
-check_discon_status_code(struct spoe_frame *frame, char **buf, char *end)
-{
- char *p = *buf;
- uint64_t sz;
- int type, ret;
-
- /* Get the "status-code" value */
- type = *p++;
- if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
- (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64)
- return -1;
- if (decode_varint(&p, end, &sz) == -1)
- return -1;
-
- frame->client->status_code = (unsigned int)sz;
-
- DEBUG(frame->worker, "<%lu> Disconnect status code : %u",
- frame->client->id, frame->client->status_code);
-
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-/* Check the disconnect message. It returns -1 if an error occurred, the number
- * of read bytes otherwise. */
-static int
-check_discon_message(struct spoe_frame *frame, char **buf, char *end)
-{
- char *str, *p = *buf;
- uint64_t sz;
- int ret;
-
- /* Get the "message" value */
- if ((*p++ & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR)
- return -1;
- ret = spoe_decode_buffer(&p, end, &str, &sz);
- if (ret == -1 || !str)
- return -1;
-
- DEBUG(frame->worker, "<%lu> Disconnect message : %.*s",
- frame->client->id, (int)sz, str);
-
- ret = (p - *buf);
- *buf = p;
- return ret;
-}
-
-
-
-/* Decode a HELLO frame received from HAProxy. It returns -1 if an error
- * occurred, otherwise the number of read bytes. HELLO frame cannot be
- * ignored and having another frame than a HELLO frame is an error. */
-static int
-handle_hahello(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
-
- p = frame->buf;
- end = frame->buf + frame->len;
-
- /* Check frame type: we really want a HELLO frame */
- if (*p++ != SPOE_FRM_T_HAPROXY_HELLO)
- goto error;
-
- DEBUG(frame->worker, "<%lu> Decode HAProxy HELLO frame", client->id);
-
- /* Retrieve flags */
- memcpy((char *)&(frame->flags), p, 4);
- frame->flags = ntohl(frame->flags);
- p += 4;
-
- /* Fragmentation is not supported for HELLO frame */
- if (!(frame->flags & SPOE_FRM_FL_FIN)) {
- client->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
- goto error;
- }
-
- /* stream-id and frame-id must be cleared */
- if (*p != 0 || *(p+1) != 0) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- p += 2;
-
- /* Loop on K/V items */
- while (p < end) {
- char *str;
- uint64_t sz;
-
- /* Decode the item name */
- spoe_decode_buffer(&p, end, &str, &sz);
- if (!str) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
-
- /* Check "supported-versions" K/V item */
- if (!memcmp(str, "supported-versions", sz)) {
- if (check_proto_version(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- /* Check "max-frame-size" K/V item */
- else if (!memcmp(str, "max-frame-size", sz)) {
- if (check_max_frame_size(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- /* Check "healthcheck" K/V item */
- else if (!memcmp(str, "healthcheck", sz)) {
- if (check_healthcheck(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- /* Check "capabilities" K/V item */
- else if (!memcmp(str, "capabilities", sz)) {
- if (check_capabilities(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- /* Check "engine-id" K/V item */
- else if (!memcmp(str, "engine-id", sz)) {
- if (check_engine_id(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- else {
- DEBUG(frame->worker, "<%lu> Skip K/V item : key=%.*s",
- client->id, (int)sz, str);
-
- /* Silently ignore unknown item */
- if (spoe_skip_data(&p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- }
-
- if (async == false || client->engine_id == NULL)
- client->async = false;
- if (pipelining == false)
- client->pipelining = false;
-
- if (client->async == true)
- use_spoe_engine(client);
-
- return (p - frame->buf);
- error:
- return -1;
-}
-
-/* Decode a DISCONNECT frame received from HAProxy. It returns -1 if an error
- * occurred, otherwise the number of read bytes. DISCONNECT frame cannot be
- * ignored and having another frame than a DISCONNECT frame is an error.*/
-static int
-handle_hadiscon(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
-
- p = frame->buf;
- end = frame->buf + frame->len;
-
- /* Check frame type: we really want a DISCONNECT frame */
- if (*p++ != SPOE_FRM_T_HAPROXY_DISCON)
- goto error;
-
- DEBUG(frame->worker, "<%lu> Decode HAProxy DISCONNECT frame", client->id);
-
- /* Retrieve flags */
- memcpy((char *)&(frame->flags), p, 4);
- frame->flags = ntohl(frame->flags);
- p += 4;
-
- /* Fragmentation is not supported for DISCONNECT frame */
- if (!(frame->flags & SPOE_FRM_FL_FIN)) {
- client->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
- goto error;
- }
-
- /* stream-id and frame-id must be cleared */
- if (*p != 0 || *(p+1) != 0) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- p += 2;
-
- client->status_code = SPOE_FRM_ERR_NONE;
-
- /* Loop on K/V items */
- while (p < end) {
- char *str;
- uint64_t sz;
-
- /* Decode item key */
- spoe_decode_buffer(&p, end, &str, &sz);
- if (!str) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
-
- /* Check "status-code" K/V item */
- if (!memcmp(str, "status-code", sz)) {
- if (check_discon_status_code(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- /* Check "message" K/V item */
- else if (!memcmp(str, "message", sz)) {
- if (check_discon_message(frame, &p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- else {
- DEBUG(frame->worker, "<%lu> Skip K/V item : key=%.*s",
- client->id, (int)sz, str);
-
- /* Silently ignore unknown item */
- if (spoe_skip_data(&p, end) == -1) {
- client->status_code = SPOE_FRM_ERR_INVALID;
- goto error;
- }
- }
- }
-
- return (p - frame->buf);
- error:
- return -1;
-}
-
-/* Decode a NOTIFY frame received from HAProxy. It returns -1 if an error
- * occurred, 0 if it must be must be ignored, otherwise the number of read
- * bytes. */
-static int
-handle_hanotify(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
- uint64_t stream_id, frame_id;
-
- p = frame->buf;
- end = frame->buf + frame->len;
-
- /* Check frame type */
- if (*p++ != SPOE_FRM_T_HAPROXY_NOTIFY)
- goto ignore;
-
- DEBUG(frame->worker, "<%lu> Decode HAProxy NOTIFY frame", client->id);
-
- /* Retrieve flags */
- memcpy((char *)&(frame->flags), p, 4);
- frame->flags = ntohl(frame->flags);
- p += 4;
-
- /* Fragmentation is not supported */
- if (!(frame->flags & SPOE_FRM_FL_FIN) && fragmentation == false) {
- client->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
- goto error;
- }
-
- /* Read the stream-id and frame-id */
- if (decode_varint(&p, end, &stream_id) == -1)
- goto ignore;
- if (decode_varint(&p, end, &frame_id) == -1)
- goto ignore;
-
- frame->stream_id = (unsigned int)stream_id;
- frame->frame_id = (unsigned int)frame_id;
-
- DEBUG(frame->worker, "<%lu> STREAM-ID=%u - FRAME-ID=%u"
- " - %s frame received"
- " - frag_len=%u - len=%u - offset=%ld",
- client->id, frame->stream_id, frame->frame_id,
- (frame->flags & SPOE_FRM_FL_FIN) ? "unfragmented" : "fragmented",
- frame->frag_len, frame->len, p - frame->buf);
-
- frame->fragmented = !(frame->flags & SPOE_FRM_FL_FIN);
- frame->offset = (p - frame->buf);
- return acc_payload(frame);
-
- ignore:
- return 0;
-
- error:
- return -1;
-}
-
-/* Decode next part of a fragmented frame received from HAProxy. It returns -1
- * if an error occurred, 0 if it must be must be ignored, otherwise the number
- * of read bytes. */
-static int
-handle_hafrag(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
- uint64_t stream_id, frame_id;
-
- p = frame->buf;
- end = frame->buf + frame->len;
-
- /* Check frame type */
- if (*p++ != SPOE_FRM_T_UNSET)
- goto ignore;
-
- DEBUG(frame->worker, "<%lu> Decode Next part of a fragmented frame", client->id);
-
- /* Fragmentation is not supported */
- if (fragmentation == false) {
- client->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
- goto error;
- }
-
- /* Retrieve flags */
- memcpy((char *)&(frame->flags), p, 4);
- frame->flags = ntohl(frame->flags);
- p+= 4;
-
- /* Read the stream-id and frame-id */
- if (decode_varint(&p, end, &stream_id) == -1)
- goto ignore;
- if (decode_varint(&p, end, &frame_id) == -1)
- goto ignore;
-
- if (frame->fragmented == false ||
- frame->stream_id != (unsigned int)stream_id ||
- frame->frame_id != (unsigned int)frame_id) {
- client->status_code = SPOE_FRM_ERR_INTERLACED_FRAMES;
- goto error;
- }
-
- if (frame->flags & SPOE_FRM_FL_ABRT) {
- DEBUG(frame->worker, "<%lu> STREAM-ID=%u - FRAME-ID=%u"
- " - Abort processing of a fragmented frame"
- " - frag_len=%u - len=%u - offset=%ld",
- client->id, frame->stream_id, frame->frame_id,
- frame->frag_len, frame->len, p - frame->buf);
- goto ignore;
- }
-
- DEBUG(frame->worker, "<%lu> STREAM-ID=%u - FRAME-ID=%u"
- " - %s fragment of a fragmented frame received"
- " - frag_len=%u - len=%u - offset=%ld",
- client->id, frame->stream_id, frame->frame_id,
- (frame->flags & SPOE_FRM_FL_FIN) ? "last" : "next",
- frame->frag_len, frame->len, p - frame->buf);
-
- frame->offset = (p - frame->buf);
- return acc_payload(frame);
-
- ignore:
- return 0;
-
- error:
- return -1;
-}
-
-/* Encode a HELLO frame to send it to HAProxy. It returns the number of written
- * bytes. */
-static int
-prepare_agenthello(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
- char capabilities[64];
- int n;
- unsigned int flags = SPOE_FRM_FL_FIN;
-
- DEBUG(frame->worker, "<%lu> Encode Agent HELLO frame", client->id);
- frame->type = SPOA_FRM_T_AGENT;
-
- p = frame->buf;
- end = frame->buf+max_frame_size;
-
- /* Frame Type */
- *p++ = SPOE_FRM_T_AGENT_HELLO;
-
- /* Set flags */
- flags = htonl(flags);
- memcpy(p, (char *)&flags, 4);
- p += 4;
-
- /* No stream-id and frame-id for HELLO frames */
- *p++ = 0;
- *p++ = 0;
-
- /* "version" K/V item */
- spoe_encode_buffer("version", 7, &p, end);
- *p++ = SPOE_DATA_T_STR;
- spoe_encode_buffer(SPOP_VERSION, SLEN(SPOP_VERSION), &p, end);
- DEBUG(frame->worker, "<%lu> Agent version : %s",
- client->id, SPOP_VERSION);
-
-
- /* "max-frame-size" K/V item */
- spoe_encode_buffer("max-frame-size", 14, &p ,end);
- *p++ = SPOE_DATA_T_UINT32;
- encode_varint(client->max_frame_size, &p, end);
- DEBUG(frame->worker, "<%lu> Agent maximum frame size : %u",
- client->id, client->max_frame_size);
-
- /* "capabilities" K/V item */
- spoe_encode_buffer("capabilities", 12, &p, end);
- *p++ = SPOE_DATA_T_STR;
-
- memset(capabilities, 0, sizeof(capabilities));
- n = 0;
-
- /* 1. Fragmentation capability ? */
- if (fragmentation == true) {
- memcpy(capabilities, "fragmentation", 13);
- n += 13;
- }
- /* 2. Pipelining capability ? */
- if (client->pipelining == true) {
- if (n) capabilities[n++] = ',';
- memcpy(capabilities + n, "pipelining", 10);
- n += 10;
- }
- /* 3. Async capability ? */
- if (client->async == true) {
- if (n) capabilities[n++] = ',';
- memcpy(capabilities + n, "async", 5);
- n += 5;
- }
- spoe_encode_buffer(capabilities, n, &p, end);
-
- DEBUG(frame->worker, "<%lu> Agent capabilities : %.*s",
- client->id, n, capabilities);
-
- frame->len = (p - frame->buf);
- return frame->len;
-}
-
-/* Encode a DISCONNECT frame to send it to HAProxy. It returns the number of
- * written bytes. */
-static int
-prepare_agentdicon(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
- char *p, *end;
- const char *reason;
- int rlen;
- unsigned int flags = SPOE_FRM_FL_FIN;
-
- DEBUG(frame->worker, "<%lu> Encode Agent DISCONNECT frame", client->id);
- frame->type = SPOA_FRM_T_AGENT;
-
- p = frame->buf;
- end = frame->buf+max_frame_size;
-
- if (client->status_code >= SPOE_FRM_ERRS)
- client->status_code = SPOE_FRM_ERR_UNKNOWN;
- reason = spoe_frm_err_reasons[client->status_code];
- rlen = strlen(reason);
-
- /* Frame type */
- *p++ = SPOE_FRM_T_AGENT_DISCON;
-
- /* Set flags */
- flags = htonl(flags);
- memcpy(p, (char *)&flags, 4);
- p += 4;
-
- /* No stream-id and frame-id for DISCONNECT frames */
- *p++ = 0;
- *p++ = 0;
-
- /* There are 2 mandatory items: "status-code" and "message" */
-
- /* "status-code" K/V item */
- spoe_encode_buffer("status-code", 11, &p, end);
- *p++ = SPOE_DATA_T_UINT32;
- encode_varint(client->status_code, &p, end);
- DEBUG(frame->worker, "<%lu> Disconnect status code : %u",
- client->id, client->status_code);
-
- /* "message" K/V item */
- spoe_encode_buffer("message", 7, &p, end);
- *p++ = SPOE_DATA_T_STR;
- spoe_encode_buffer(reason, rlen, &p, end);
- DEBUG(frame->worker, "<%lu> Disconnect message : %s",
- client->id, reason);
-
- frame->len = (p - frame->buf);
- return frame->len;
-}
-
-/* Encode a ACK frame to send it to HAProxy. It returns the number of written
- * bytes. */
-static int
-prepare_agentack(struct spoe_frame *frame)
-{
- char *p, *end;
- unsigned int flags = SPOE_FRM_FL_FIN;
-
- /* Be careful here, in async mode, frame->client can be NULL */
-
- DEBUG(frame->worker, "Encode Agent ACK frame");
- frame->type = SPOA_FRM_T_AGENT;
-
- p = frame->buf;
- end = frame->buf+max_frame_size;
-
- /* Frame type */
- *p++ = SPOE_FRM_T_AGENT_ACK;
-
- /* Set flags */
- flags = htonl(flags);
- memcpy(p, (char *)&flags, 4);
- p += 4;
-
- /* Set stream-id and frame-id for ACK frames */
- encode_varint(frame->stream_id, &p, end);
- encode_varint(frame->frame_id, &p, end);
-
- DEBUG(frame->worker, "STREAM-ID=%u - FRAME-ID=%u",
- frame->stream_id, frame->frame_id);
-
- frame->len = (p - frame->buf);
- return frame->len;
-}
-
-static int
-create_server_socket(void)
-{
- struct sockaddr_in listen_addr;
- int fd, yes = 1;
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- LOG(&null_worker, "Failed to create service socket : %m");
- return -1;
- }
-
- memset(&listen_addr, 0, sizeof(listen_addr));
- listen_addr.sin_family = AF_INET;
- listen_addr.sin_addr.s_addr = INADDR_ANY;
- listen_addr.sin_port = htons(server_port);
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0 ||
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) < 0) {
- LOG(&null_worker, "Failed to set option on server socket : %m");
- return -1;
- }
-
- if (bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0) {
- LOG(&null_worker, "Failed to bind server socket : %m");
- return -1;
- }
-
- if (listen(fd, CONNECTION_BACKLOG) < 0) {
- LOG(&null_worker, "Failed to listen on server socket : %m");
- return -1;
- }
-
- return fd;
-}
-
-static void
-release_frame(struct spoe_frame *frame)
-{
- struct worker *worker;
-
- if (frame == NULL)
- return;
-
- if (event_pending(&frame->process_frame_event, EV_TIMEOUT, NULL))
- event_del(&frame->process_frame_event);
-
- worker = frame->worker;
- LIST_DELETE(&frame->list);
- if (frame->frag_buf)
- free(frame->frag_buf);
- memset(frame, 0, sizeof(*frame)+max_frame_size+4);
- LIST_APPEND(&worker->frames, &frame->list);
-}
-
-static void
-release_client(struct client *c)
-{
- struct spoe_frame *frame, *back;
-
- if (c == NULL)
- return;
-
- DEBUG(c->worker, "<%lu> Release client", c->id);
-
- LIST_DELETE(&c->by_worker);
- c->worker->nbclients--;
-
- unuse_spoe_engine(c);
- free(c->engine_id);
-
- if (event_pending(&c->read_frame_event, EV_READ, NULL))
- event_del(&c->read_frame_event);
- if (event_pending(&c->write_frame_event, EV_WRITE, NULL))
- event_del(&c->write_frame_event);
-
- release_frame(c->incoming_frame);
- release_frame(c->outgoing_frame);
- list_for_each_entry_safe(frame, back, &c->processing_frames, list) {
- release_frame(frame);
- }
- list_for_each_entry_safe(frame, back, &c->outgoing_frames, list) {
- release_frame(frame);
- }
-
- if (c->fd >= 0)
- close(c->fd);
-
- free(c);
-}
-
-static void
-reset_frame(struct spoe_frame *frame)
-{
- if (frame == NULL)
- return;
-
- if (frame->frag_buf)
- free(frame->frag_buf);
-
- frame->type = SPOA_FRM_T_UNKNOWN;
- frame->buf = (char *)(frame->data);
- frame->offset = 0;
- frame->len = 0;
- frame->stream_id = 0;
- frame->frame_id = 0;
- frame->flags = 0;
- frame->hcheck = false;
- frame->fragmented = false;
- frame->modsec_code = -1;
- frame->frag_buf = NULL;
- frame->frag_len = 0;
- LIST_INIT(&frame->list);
-}
-
-static void
-use_spoe_engine(struct client *client)
-{
- struct spoe_engine *eng;
-
- if (client->engine_id == NULL)
- return;
-
- list_for_each_entry(eng, &client->worker->engines, list) {
- if (strcmp(eng->id, client->engine_id) == 0)
- goto end;
- }
-
- if ((eng = malloc(sizeof(*eng))) == NULL) {
- client->async = false;
- return;
- }
-
- eng->id = strdup(client->engine_id);
- LIST_INIT(&eng->clients);
- LIST_INIT(&eng->processing_frames);
- LIST_INIT(&eng->outgoing_frames);
- LIST_APPEND(&client->worker->engines, &eng->list);
- LOG(client->worker, "Add new SPOE engine '%s'", eng->id);
-
- end:
- client->engine = eng;
- LIST_APPEND(&eng->clients, &client->by_engine);
-}
-
-static void
-unuse_spoe_engine(struct client *client)
-{
- struct spoe_engine *eng;
- struct spoe_frame *frame, *back;
-
- if (client == NULL || client->engine == NULL)
- return;
-
- eng = client->engine;
- client->engine = NULL;
- LIST_DELETE(&client->by_engine);
- if (!LIST_ISEMPTY(&eng->clients))
- return;
-
- LOG(client->worker, "Remove SPOE engine '%s'", eng->id);
- LIST_DELETE(&eng->list);
-
- list_for_each_entry_safe(frame, back, &eng->processing_frames, list) {
- release_frame(frame);
- }
- list_for_each_entry_safe(frame, back, &eng->outgoing_frames, list) {
- release_frame(frame);
- }
- free(eng->id);
- free(eng);
-}
-
-
-static struct spoe_frame *
-acquire_incoming_frame(struct client *client)
-{
- struct spoe_frame *frame;
-
- frame = client->incoming_frame;
- if (frame != NULL)
- return frame;
-
- if (LIST_ISEMPTY(&client->worker->frames)) {
- if ((frame = calloc(1, sizeof(*frame)+max_frame_size+4)) == NULL) {
- LOG(client->worker, "Failed to allocate new frame : %m");
- return NULL;
- }
- }
- else {
- frame = LIST_NEXT(&client->worker->frames, typeof(frame), list);
- LIST_DELETE(&frame->list);
- }
-
- reset_frame(frame);
- frame->worker = client->worker;
- frame->engine = client->engine;
- frame->client = client;
-
- if (event_assign(&frame->process_frame_event, client->worker->base, -1,
- EV_TIMEOUT|EV_PERSIST, process_frame_cb, frame) < 0) {
- LOG(client->worker, "Failed to create frame event");
- return NULL;
- }
-
- client->incoming_frame = frame;
- return frame;
-}
-
-static struct spoe_frame *
-acquire_outgoing_frame(struct client *client)
-{
- struct spoe_engine *engine = client->engine;
- struct spoe_frame *frame = NULL;
-
- if (client->outgoing_frame != NULL)
- frame = client->outgoing_frame;
- else if (!LIST_ISEMPTY(&client->outgoing_frames)) {
- frame = LIST_NEXT(&client->outgoing_frames, typeof(frame), list);
- LIST_DELETE(&frame->list);
- client->outgoing_frame = frame;
- }
- else if (engine!= NULL && !LIST_ISEMPTY(&engine->outgoing_frames)) {
- frame = LIST_NEXT(&engine->outgoing_frames, typeof(frame), list);
- LIST_DELETE(&frame->list);
- client->outgoing_frame = frame;
- }
- return frame;
-}
-
-static void
-write_frame(struct client *client, struct spoe_frame *frame)
-{
- uint32_t netint;
-
- LIST_DELETE(&frame->list);
-
- frame->buf = (char *)(frame->data);
- frame->offset = 0;
- netint = htonl(frame->len);
- memcpy(frame->buf, &netint, 4);
-
- if (client != NULL) { /* HELLO or DISCONNECT frames */
- event_add(&client->write_frame_event, NULL);
-
- /* Try to process the frame as soon as possible, and always
- * attach it to the client */
- if (client->async || client->pipelining) {
- if (client->outgoing_frame == NULL)
- client->outgoing_frame = frame;
- else
- LIST_INSERT(&client->outgoing_frames, &frame->list);
- }
- else {
- client->outgoing_frame = frame;
- event_del(&client->read_frame_event);
- }
- }
- else { /* for all other frames */
- if (frame->client == NULL) { /* async mode ! */
- LIST_APPEND(&frame->engine->outgoing_frames, &frame->list);
- list_for_each_entry(client, &frame->engine->clients, by_engine)
- event_add(&client->write_frame_event, NULL);
- }
- else if (frame->client->pipelining) {
- LIST_APPEND(&frame->client->outgoing_frames, &frame->list);
- event_add(&frame->client->write_frame_event, NULL);
- }
- else {
- frame->client->outgoing_frame = frame;
- event_add(&frame->client->write_frame_event, NULL);
- event_del(&frame->client->read_frame_event);
- }
- }
-}
-
-static void
-process_incoming_frame(struct spoe_frame *frame)
-{
- struct client *client = frame->client;
-
- if (event_add(&frame->process_frame_event, &processing_delay) < 0) {
- LOG(client->worker, "Failed to process incoming frame");
- release_frame(frame);
- return;
- }
-
- if (client->async) {
- frame->client = NULL;
- LIST_APPEND(&frame->engine->processing_frames, &frame->list);
- }
- else if (client->pipelining)
- LIST_APPEND(&client->processing_frames, &frame->list);
- else
- event_del(&client->read_frame_event);
-}
-
-static void
-signal_cb(evutil_socket_t sig, short events, void *user_data)
-{
- struct event_base *base = user_data;
- int i;
-
- DEBUG(&null_worker, "Stopping the server");
-
- event_base_loopbreak(base);
- DEBUG(&null_worker, "Main event loop stopped");
-
- for (i = 0; i < num_workers; i++) {
- event_base_loopbreak(workers[i].base);
- DEBUG(&null_worker, "Event loop stopped for worker %02d",
- workers[i].id);
- }
-}
-
-static void
-worker_monitor_cb(evutil_socket_t fd, short events, void *arg)
-{
- struct worker *worker = arg;
-
- LOG(worker, "%u clients connected", worker->nbclients);
-}
-
-static void
-process_frame_cb(evutil_socket_t fd, short events, void *arg)
-{
- struct spoe_frame *frame = arg;
- char *p, *end;
- int ret;
-
- DEBUG(frame->worker,
- "Process frame messages : STREAM-ID=%u - FRAME-ID=%u - length=%u bytes",
- frame->stream_id, frame->frame_id, frame->len - frame->offset);
-
- p = frame->buf + frame->offset;
- end = frame->buf + frame->len;
-
- /* Loop on messages */
- while (p < end) {
- char *str;
- uint64_t sz;
- int nbargs;
-
- /* Decode the message name */
- spoe_decode_buffer(&p, end, &str, &sz);
- if (!str)
- goto stop_processing;
-
- DEBUG(frame->worker, "Process SPOE Message '%.*s'", (int)sz, str);
-
- nbargs = *p++; /* Get the number of arguments */
- frame->offset = (p - frame->buf); /* Save index to handle errors and skip args */
- if (!memcmp(str, "check-request", sz)) {
- struct modsecurity_parameters params;
-
- memset(¶ms, 0, sizeof(params));
-
- if (nbargs != 8)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode unique id. */
- if (spoe_decode_data(&p, end, ¶ms.uniqueid) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode method. */
- if (spoe_decode_data(&p, end, ¶ms.method) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode path. */
- if (spoe_decode_data(&p, end, ¶ms.path) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode query. */
- if (spoe_decode_data(&p, end, ¶ms.query) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode protocol version. */
- if (spoe_decode_data(&p, end, ¶ms.vers) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode hdrs. */
- if (spoe_decode_data(&p, end, ¶ms.hdrs_bin) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode body length. */
- if (spoe_decode_data(&p, end, ¶ms.body_length) == -1)
- goto skip_message;
-
- /* Decode parameter name. */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
-
- /* Decode body. */
- if (spoe_decode_data(&p, end, ¶ms.body) == -1)
- goto skip_message;
-
- frame->modsec_code = modsecurity_process(frame->worker, ¶ms);
- }
- else {
- skip_message:
- p = frame->buf + frame->offset; /* Restore index */
-
- while (nbargs-- > 0) {
- /* Silently ignore argument: its name and its value */
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_skip_data(&p, end) == -1)
- goto stop_processing;
- }
- }
- }
-
- stop_processing:
- /* Prepare agent ACK frame */
- frame->buf = (char *)(frame->data) + 4;
- frame->offset = 0;
- frame->len = 0;
- frame->flags = 0;
-
- ret = prepare_agentack(frame);
- p = frame->buf + ret;
- end = frame->buf+max_frame_size;
-
- if (frame->modsec_code != -1) {
- DEBUG(frame->worker, "Add action : set variable code=%u",
- frame->modsec_code);
-
- *p++ = SPOE_ACT_T_SET_VAR; /* Action type */
- *p++ = 3; /* Number of args */
- *p++ = SPOE_SCOPE_TXN; /* Arg 1: the scope */
- spoe_encode_buffer("code", 8, &p, end); /* Arg 2: variable name */
- *p++ = SPOE_DATA_T_UINT32;
- encode_varint(frame->modsec_code, &p, end); /* Arg 3: variable value */
- frame->len = (p - frame->buf);
- }
- write_frame(NULL, frame);
-}
-
-static void
-read_frame_cb(evutil_socket_t fd, short events, void *arg)
-{
- struct client *client = arg;
- struct spoe_frame *frame;
- uint32_t netint;
- int n;
-
- DEBUG(client->worker, "<%lu> %s", client->id, __FUNCTION__);
- if ((frame = acquire_incoming_frame(client)) == NULL)
- goto close;
-
- frame->type = SPOA_FRM_T_HAPROXY;
- if (frame->buf == (char *)(frame->data)) {
- /* Read the frame length: frame->buf points on length part (frame->data) */
- n = read(client->fd, frame->buf+frame->offset, 4-frame->offset);
- if (n <= 0) {
- if (n < 0)
- LOG(client->worker, "Failed to read frame length : %m");
- goto close;
- }
- frame->offset += n;
- if (frame->offset != 4)
- return;
- memcpy(&netint, frame->buf, 4);
- frame->buf += 4;
- frame->offset = 0;
- frame->len = ntohl(netint);
- }
-
- /* Read the frame: frame->buf points on frame part (frame->data+4)*/
- n = read(client->fd, frame->buf + frame->offset,
- frame->len - frame->offset);
- if (n <= 0) {
- if (n < 0) {
- LOG(client->worker, "Frame to read frame : %m");
- goto close;
- }
- return;
- }
- frame->offset += n;
- if (frame->offset != frame->len)
- return;
- frame->offset = 0;
-
- DEBUG(client->worker, "<%lu> New Frame of %u bytes received",
- client->id, frame->len);
-
- switch (client->state) {
- case SPOA_ST_CONNECTING:
- if (handle_hahello(frame) < 0) {
- LOG(client->worker, "Failed to decode HELLO frame");
- goto disconnect;
- }
- prepare_agenthello(frame);
- goto write_frame;
-
- case SPOA_ST_PROCESSING:
- if (frame->buf[0] == SPOE_FRM_T_HAPROXY_DISCON) {
- client->state = SPOA_ST_DISCONNECTING;
- goto disconnecting;
- }
- if (frame->buf[0] == SPOE_FRM_T_UNSET)
- n = handle_hafrag(frame);
- else
- n = handle_hanotify(frame);
-
- if (n < 0) {
- LOG(client->worker, "Failed to decode frame: %s",
- spoe_frm_err_reasons[client->status_code]);
- goto disconnect;
- }
- else if (n == 0) {
- LOG(client->worker, "Ignore invalid/unknown/aborted frame");
- goto ignore_frame;
- }
- else if (n == 1)
- goto noop;
- else
- goto process_frame;
-
- case SPOA_ST_DISCONNECTING:
- disconnecting:
- if (handle_hadiscon(frame) < 0) {
- LOG(client->worker, "Failed to decode DISCONNECT frame");
- goto disconnect;
- }
- if (client->status_code != SPOE_FRM_ERR_NONE)
- LOG(client->worker, "<%lu> Peer closed connection: %s",
- client->id, spoe_frm_err_reasons[client->status_code]);
- goto disconnect;
- }
-
- noop:
- return;
-
- ignore_frame:
- reset_frame(frame);
- return;
-
- process_frame:
- process_incoming_frame(frame);
- client->incoming_frame = NULL;
- return;
-
- write_frame:
- write_frame(client, frame);
- client->incoming_frame = NULL;
- return;
-
- disconnect:
- client->state = SPOA_ST_DISCONNECTING;
- if (prepare_agentdicon(frame) < 0) {
- LOG(client->worker, "Failed to encode DISCONNECT frame");
- goto close;
- }
- goto write_frame;
-
- close:
- release_client(client);
-}
-
-static void
-write_frame_cb(evutil_socket_t fd, short events, void *arg)
-{
- struct client *client = arg;
- struct spoe_frame *frame;
- int n;
-
- DEBUG(client->worker, "<%lu> %s", client->id, __FUNCTION__);
- if ((frame = acquire_outgoing_frame(client)) == NULL) {
- event_del(&client->write_frame_event);
- return;
- }
-
- if (frame->buf == (char *)(frame->data)) {
- /* Write the frame length: frame->buf points on length part (frame->data) */
- n = write(client->fd, frame->buf+frame->offset, 4-frame->offset);
- if (n <= 0) {
- if (n < 0)
- LOG(client->worker, "Failed to write frame length : %m");
- goto close;
- }
- frame->offset += n;
- if (frame->offset != 4)
- return;
- frame->buf += 4;
- frame->offset = 0;
- }
-
- /* Write the frame: frame->buf points on frame part (frame->data+4)*/
- n = write(client->fd, frame->buf + frame->offset,
- frame->len - frame->offset);
- if (n <= 0) {
- if (n < 0) {
- LOG(client->worker, "Failed to write frame : %m");
- goto close;
- }
- return;
- }
- frame->offset += n;
- if (frame->offset != frame->len)
- return;
-
- DEBUG(client->worker, "<%lu> Frame of %u bytes send",
- client->id, frame->len);
-
- switch (client->state) {
- case SPOA_ST_CONNECTING:
- if (frame->hcheck == true) {
- DEBUG(client->worker,
- "<%lu> Close client after healthcheck",
- client->id);
- goto close;
- }
- client->state = SPOA_ST_PROCESSING;
- break;
-
- case SPOA_ST_PROCESSING:
- break;
-
- case SPOA_ST_DISCONNECTING:
- goto close;
- }
-
- release_frame(frame);
- client->outgoing_frame = NULL;
- if (!client->async && !client->pipelining) {
- event_del(&client->write_frame_event);
- event_add(&client->read_frame_event, NULL);
- }
- return;
-
- close:
- release_client(client);
-}
-
-static void
-accept_cb(int listener, short event, void *arg)
-{
- struct worker *worker;
- struct client *client;
- int fd;
-
- worker = &workers[clicount++ % num_workers];
-
- if ((fd = accept(listener, NULL, NULL)) < 0) {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- LOG(worker, "Failed to accept client connection : %m");
- return;
- }
-
- DEBUG(&null_worker,
- "<%lu> New Client connection accepted and assigned to worker %02d",
- clicount, worker->id);
-
- if (evutil_make_socket_nonblocking(fd) < 0) {
- LOG(&null_worker, "Failed to set client socket to non-blocking : %m");
- close(fd);
- return;
- }
-
- if ((client = calloc(1, sizeof(*client))) == NULL) {
- LOG(&null_worker, "Failed to allocate memory for client state : %m");
- close(fd);
- return;
- }
-
- client->id = clicount;
- client->fd = fd;
- client->worker = worker;
- client->state = SPOA_ST_CONNECTING;
- client->status_code = SPOE_FRM_ERR_NONE;
- client->max_frame_size = max_frame_size;
- client->engine = NULL;
- client->pipelining = false;
- client->async = false;
- client->incoming_frame = NULL;
- client->outgoing_frame = NULL;
- LIST_INIT(&client->processing_frames);
- LIST_INIT(&client->outgoing_frames);
-
- LIST_APPEND(&worker->clients, &client->by_worker);
-
- worker->nbclients++;
-
- if (event_assign(&client->read_frame_event, worker->base, fd,
- EV_READ|EV_PERSIST, read_frame_cb, client) < 0 ||
- event_assign(&client->write_frame_event, worker->base, fd,
- EV_WRITE|EV_PERSIST, write_frame_cb, client) < 0) {
- LOG(&null_worker, "Failed to create client events");
- release_client(client);
- return;
- }
- event_add(&client->read_frame_event, NULL);
-}
-
-static void *
-worker_function(void *data)
-{
- struct client *client, *cback;
- struct spoe_frame *frame, *fback;
- struct worker *worker = data;
-
- DEBUG(worker, "Worker ready to process client messages");
- event_base_dispatch(worker->base);
-
- list_for_each_entry_safe(client, cback, &worker->clients, by_worker) {
- release_client(client);
- }
-
- list_for_each_entry_safe(frame, fback, &worker->frames, list) {
- LIST_DELETE(&frame->list);
- free(frame);
- }
-
- event_free(worker->monitor_event);
- event_base_free(worker->base);
- DEBUG(worker, "Worker is stopped");
- pthread_exit(&null_worker);
-}
-
-
-static int
-parse_processing_delay(const char *str)
-{
- unsigned long value;
-
- value = 0;
- while (1) {
- unsigned int j;
-
- j = *str - '0';
- if (j > 9)
- break;
- str++;
- value *= 10;
- value += j;
- }
-
- switch (*str) {
- case '\0': /* no unit = millisecond */
- value *= 1000;
- break;
- case 's': /* second */
- value *= 1000000;
- str++;
- break;
- case 'm': /* millisecond : "ms" */
- if (str[1] != 's')
- return -1;
- value *= 1000;
- str += 2;
- break;
- case 'u': /* microsecond : "us" */
- if (str[1] != 's')
- return -1;
- str += 2;
- break;
- default:
- return -1;
- }
- if (*str)
- return -1;
-
- processing_delay.tv_sec = (time_t)(value / 1000000);
- processing_delay.tv_usec = (suseconds_t)(value % 1000000);
- return 0;
-}
-
-
-static void
-usage(char *prog)
-{
- fprintf(stderr,
- "Usage : %s [OPTION]...\n"
- " -h Print this message\n"
- " -d Enable the debug mode\n"
- " -f <config-file> ModSecurity configuration file\n"
- " -m <max-frame-size> Specify the maximum frame size (default : %u)\n"
- " -p <port> Specify the port to listen on (default : %d)\n"
- " -n <num-workers> Specify the number of workers (default : %d)\n"
- " -c <capability> Enable the support of the specified capability\n"
- " -t <time> Set a delay to process a message (default: 0)\n"
- " The value is specified in milliseconds by default,\n"
- " but can be in any other unit if the number is suffixed\n"
- " by a unit (us, ms, s)\n"
- "\n"
- " Supported capabilities: fragmentation, pipelining, async\n",
- prog, MAX_FRAME_SIZE, DEFAULT_PORT, NUM_WORKERS);
-}
-
-int
-main(int argc, char **argv)
-{
- struct event_base *base = NULL;
- struct event *signal_event = NULL, *accept_event = NULL;
- int opt, i, fd = -1;
- const char *configuration_file = NULL;
-
- // TODO: add '-t <processing-time>' option
- while ((opt = getopt(argc, argv, "hdm:n:p:c:t:f:")) != -1) {
- switch (opt) {
- case 'h':
- usage(argv[0]);
- return EXIT_SUCCESS;
- case 'd':
- debug = true;
- break;
- case 'm':
- max_frame_size = atoi(optarg);
- break;
- case 'n':
- num_workers = atoi(optarg);
- break;
- case 'p':
- server_port = atoi(optarg);
- break;
- case 'f':
- configuration_file = optarg;
- break;
- case 'c':
- if (strcmp(optarg, "pipelining") == 0)
- pipelining = true;
- else if (strcmp(optarg, "async") == 0)
- async = true;
- else if (strcmp(optarg, "fragmentation") == 0)
- fragmentation = true;
- else
- fprintf(stderr, "WARNING: unsupported capability '%s'\n", optarg);
- break;
- case 't':
- if (!parse_processing_delay(optarg))
- break;
- fprintf(stderr, "%s: failed to parse time '%s'.\n", argv[0], optarg);
- fprintf(stderr, "Try '%s -h' for more information.\n", argv[0]);
- return EXIT_FAILURE;
- default:
- usage(argv[0]);
- return EXIT_FAILURE;
- }
- }
-
- if (!configuration_file) {
- LOG(&null_worker, "ModSecurity configuration is required.\n");
- goto error;
- }
-
- if (modsecurity_load(configuration_file) == -1)
- goto error;
-
- if (num_workers <= 0) {
- LOG(&null_worker, "%s : Invalid number of workers '%d'\n",
- argv[0], num_workers);
- goto error;
- }
-
- if (server_port <= 0) {
- LOG(&null_worker, "%s : Invalid port '%d'\n",
- argv[0], server_port);
- goto error;
- }
-
-
- if (evthread_use_pthreads() < 0) {
- LOG(&null_worker, "No pthreads support for libevent");
- goto error;
- }
-
- if ((base = event_base_new()) == NULL) {
- LOG(&null_worker, "Failed to initialize libevent : %m");
- goto error;
- }
-
- signal(SIGPIPE, SIG_IGN);
-
- if ((fd = create_server_socket()) < 0) {
- LOG(&null_worker, "Failed to create server socket");
- goto error;
- }
- if (evutil_make_socket_nonblocking(fd) < 0) {
- LOG(&null_worker, "Failed to set server socket to non-blocking");
- goto error;
- }
-
- if ((workers = calloc(num_workers, sizeof(*workers))) == NULL) {
- LOG(&null_worker, "Failed to set allocate memory for workers");
- goto error;
- }
-
- for (i = 0; i < num_workers; ++i) {
- struct worker *w = &workers[i];
-
- w->id = i+1;
- w->nbclients = 0;
- LIST_INIT(&w->engines);
- LIST_INIT(&w->clients);
- LIST_INIT(&w->frames);
-
- if ((w->base = event_base_new()) == NULL) {
- LOG(&null_worker,
- "Failed to initialize libevent for worker %02d : %m",
- w->id);
- goto error;
- }
-
- w->monitor_event = event_new(w->base, fd, EV_PERSIST,
- worker_monitor_cb, (void *)w);
- if (w->monitor_event == NULL ||
- event_add(w->monitor_event, (struct timeval[]){{5,0}}) < 0) {
- LOG(&null_worker,
- "Failed to create monitor event for worker %02d",
- w->id);
- goto error;
- }
-
- if (pthread_create(&w->thread, NULL, worker_function, (void *)w)) {
- LOG(&null_worker,
- "Failed to start thread for worker %02d : %m",
- w->id);
- }
- DEBUG(&null_worker, "Worker %02d initialized", w->id);
- }
-
- accept_event = event_new(base, fd, EV_READ|EV_PERSIST, accept_cb,
- (void *)base);
- if (accept_event == NULL || event_add(accept_event, NULL) < 0) {
- LOG(&null_worker, "Failed to create accept event : %m");
- }
-
- signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
- if (signal_event == NULL || event_add(signal_event, NULL) < 0) {
- LOG(&null_worker, "Failed to create signal event : %m");
- }
-
- DEBUG(&null_worker,
- "Server is ready"
- " [fragmentation=%s - pipelining=%s - async=%s - debug=%s - max-frame-size=%u]",
- (fragmentation?"true":"false"), (pipelining?"true":"false"), (async?"true":"false"),
- (debug?"true":"false"), max_frame_size);
- event_base_dispatch(base);
-
- for (i = 0; i < num_workers; i++) {
- struct worker *w = &workers[i];
-
- pthread_join(w->thread, NULL);
- DEBUG(&null_worker, "Worker %02d terminated", w->id);
- }
-
- free(workers);
- event_free(signal_event);
- event_free(accept_event);
- event_base_free(base);
- close(fd);
- return EXIT_SUCCESS;
-
- error:
- if (workers != NULL)
- free(workers);
- if (signal_event != NULL)
- event_free(signal_event);
- if (accept_event != NULL)
- event_free(accept_event);
- if (base != NULL)
- event_base_free(base);
- if (fd != -1)
- close(fd);
- return EXIT_FAILURE;
-}
+++ /dev/null
-/*
- * 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;