dev/tcploop/tcploop
dev/hpack/decode
dev/hpack/gen-rht
-contrib/mod_defender/defender
/src/dlmalloc.c
/tests/test_hashes
+++ /dev/null
-DESTDIR =
-PREFIX = /usr/local
-BINDIR = $(PREFIX)/bin
-
-CC ?= gcc
-LD = $(CC)
-
-CXX ?= g++
-
-ifeq ($(MOD_DEFENDER_SRC),)
-MOD_DEFENDER_SRC := ./mod_defender_src
-endif
-
-ifeq ($(APACHE2_INC),)
-APACHE2_INC := /usr/include/apache2
-endif
-
-ifeq ($(APR_INC),)
-APR_INC := /usr/include/apr-1.0
-endif
-
-ifeq ($(EVENT_LIB),)
-EVENT_LIB := -levent
-endif
-
-ifeq ($(EVENT_INC),)
-EVENT_INC := /usr/include
-endif
-
-CFLAGS += -g -Wall -pthread
-INCS += -Iinclude -I$(MOD_DEFENDER_SRC) -I$(APACHE2_INC) -I$(APR_INC) -I$(EVENT_INC)
-LIBS += -lpthread $(EVENT_LIB) -levent_pthreads -lapr-1 -laprutil-1 -lstdc++ -lm
-
-CXXFLAGS = -g -std=gnu++11
-CXXINCS += -I$(MOD_DEFENDER_SRC) -I$(MOD_DEFENDER_SRC)/deps -I$(APACHE2_INC) -I$(APR_INC)
-
-SRCS = standalone.o spoa.o defender.o \
- $(wildcard $(MOD_DEFENDER_SRC)/deps/libinjection/*.c)
-OBJS = $(patsubst %.c, %.o, $(SRCS))
-
-CXXSRCS = $(wildcard $(MOD_DEFENDER_SRC)/*.cpp)
-CXXOBJS = $(patsubst %.cpp, %.o, $(CXXSRCS))
-
-defender: $(OBJS) $(CXXOBJS)
- $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
-
-install: defender
- install defender $(DESTDIR)$(BINDIR)
-
-clean:
- rm -f defender $(OBJS) $(CXXOBJS)
-
-%.o: %.c
- $(CC) $(CFLAGS) $(INCS) -c -o $@ $<
-
-%.o: %.cpp
- $(CXX) $(CXXFLAGS) $(CXXINCS) -c -o $@ $<
+++ /dev/null
- --------------------------
- Mod Defender for HAProxy
- --------------------------
-
-
-This is a service that talks SPOE protocol and uses the Mod Defender
-(https://github.com/VultureProject/mod_defender) functionality to detect
-HTTP attacks. It returns a HTTP status code to indicate whether the request
-is suspicious or not, based on NAXSI rules. The value of the returned code
-can be used in HAProxy rules to determine if the HTTP request should be
-blocked/rejected.
-
-Unlike ModSecurity, Mod Defender is a whitelist based WAF (everything is
-disallowed, unless there are rules saying otherwise). It's a partial
-replication of NAXSI and it uses NAXSI compatible rules configuration
-format.
-
-
-1) How to build it
-------------------
-
-Required packages :
-
- * Mod Defender source (https://github.com/VultureProject/mod_defender)
- * Asynchronous event notification library and headers (libevent)
- * Apache 2 (>= 2.4) development headers
- * APR library and headers
- * GNU C (gcc) and C++ (g++) >= 4.9
- * GNU Standard C++ Library v3 (libstdc++)
- * GNU Make
-
-
-Compile the source :
-
- $ make MOD_DEFENDER_SRC=/path/to/mod_defender_src
-
-
-2) Configuration
-----------------
-
-Download the Naxsi core rules file :
-
- $ wget -O /path/to/core.rules \
- https://raw.githubusercontent.com/nbs-system/naxsi/master/naxsi_config/naxsi_core.rules
-
-
-Create the Mod Defender configuration file. For example :
-
- # Defender toggle
- Defender On
- # Match log path
- MatchLog /path/to/defender_match.log
- # JSON Match log path
- JSONMatchLog /path/to/defender_json_match.log
- # Request body limit
- RequestBodyLimit 8388608
- # Learning mode toggle
- LearningMode Off
- # Extensive Learning log toggle
- ExtensiveLog Off
- # Libinjection SQL toggle
- LibinjectionSQL On
- # Libinjection XSS toggle
- LibinjectionXSS On
-
- # Rules
- Include /path/to/core.rules
-
- # Score action
- CheckRule "$SQL >= 8" BLOCK
- CheckRule "$RFI >= 8" BLOCK
- CheckRule "$TRAVERSAL >= 4" BLOCK
- CheckRule "$EVADE >= 4" BLOCK
- CheckRule "$XSS >= 8" BLOCK
- CheckRule "$UPLOAD >= 8" BLOCK
-
- # Whitelists
- # ....
-
-
-Next step is to configure the SPOE for use with the Mod Defender service.
-Example configuration (args elements order is important) :
-
- [mod_defender]
-
- spoe-agent mod-defender-agent
- messages check-request
- option var-prefix defender
- timeout hello 100ms
- timeout idle 30s
- timeout processing 15ms
- use-backend spoe-mod-defender
-
- spoe-message check-request
- args src unique-id method path query req.ver req.hdrs_bin req.body
- event on-frontend-http-request
-
-
-The engine is in the scope "mod_defender". To enable it, you must set the
-following line in a frontend/listener section :
-
- frontend my_frontend
- ...
- filter spoe engine mod_defender config /path/to/spoe-mod-defender.conf
- ...
-
-
-Also, we must define the "spoe-mod-defender" backend in HAProxy configuration :
-
- backend spoe-mod-defender
- mode tcp
- balance roundrobin
- timeout connect 5s
- timeout server 3m
- server defender1 127.0.0.1:12345
-
-
-The Mod Defender status is returned in a variable "sess.defender.status" --
-it contains the returned HTTP status code. The request is considered
-malicious if the variable contains value greater than zero.
-
-The following rule can be used to reject all suspicious HTTP requests :
-
- http-request deny if { var(sess.defender.status) -m int gt 0 }
-
-
-3) Start the service
---------------------
-
-To start the service, you need to use "defender" binary :
-
- $ ./defender -h
- Usage : ./defender [OPTION]...
- -h Print this message
- -f <config-file> Mod Defender configuration file
- -l <log-file> Mod Defender log file
- -d Enable the debug mode
- -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 : 10)
- -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)
-
- Supported capabilities: fragmentation, pipelining, async
-
-Example:
-
- $ ./defender -n 4 -f /path/to/mod_defender.conf -d -l /path/to/error.log
-
-
-4) Known bugs and limitations
------------------------------
-
-In its current state, the module is limited by haproxy to the analysis of
-the first buffer. One workaround may consist in significantly increasing
-haproxy's buffer size.
+++ /dev/null
-/*
- * Mod Defender for HAProxy
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
- *
- * Mod Defender
- * Copyright (c) 2017 Annihil (https://github.com/VultureProject/mod_defender)
- *
- * Parts of code based on Apache HTTP Server source
- * Copyright 2015 The Apache Software Foundation (http://www.apache.org/)
- *
- * 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
- * 3 of the License, or (at your option) any later version.
- *
- */
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <haproxy/spoe.h>
-#include <haproxy/tools.h>
-
-#include <http_core.h>
-#include <http_main.h>
-#include <http_log.h>
-#include <http_request.h>
-
-#include <apr_pools.h>
-#include <apr_strings.h>
-
-#include "spoa.h"
-#include "standalone.h"
-#include "defender.h"
-
-#define DEFENDER_NAME "defender"
-#define DEFENDER_INPUT_FILTER "DEFENDER_IN"
-#define DEFENDER_DEFAULT_UNIQUE_ID "unique_id"
-#define DEFENDER_BRIGADE_REQUEST "defender-brigade-request"
-
-extern module AP_MODULE_DECLARE_DATA defender_module;
-
-DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s))
-DECLARE_HOOK(int,fixups,(request_rec *r))
-DECLARE_HOOK(int,header_parser,(request_rec *r))
-
-char *defender_name = DEFENDER_NAME;
-const char *defender_argv[] = { DEFENDER_NAME, NULL };
-const char *defender_unknown_hostname = "";
-
-void *defender_module_config = NULL;
-static server_rec *server = NULL;
-apr_pool_t *defender_pool = NULL;
-
-char hostname[MAX_HOSTNAME_LEN];
-char defender_cwd[MAXPATHLEN];
-
-static apr_status_t defender_bucket_read(apr_bucket *b, const char **str,
- apr_size_t *len, apr_read_type_e block);
-static void defender_bucket_destroy(void *data);
-
-static const apr_bucket_type_t apr_bucket_type_defender = {
- "defender", 8, APR_BUCKET_DATA,
- defender_bucket_destroy,
- defender_bucket_read,
- apr_bucket_setaside_noop,
- apr_bucket_shared_split,
- apr_bucket_shared_copy
-};
-
-struct apr_bucket_defender {
- apr_bucket_refcount refcount;
- struct buffer buf;
-};
-
-static apr_status_t defender_bucket_read(apr_bucket *b, const char **str,
- apr_size_t *len, apr_read_type_e block)
-{
- struct apr_bucket_defender *d = b->data;
-
- *str = d->buf.area;
- *len = d->buf.data;
-
- return APR_SUCCESS;
-}
-
-static void defender_bucket_destroy(void *data)
-{
- struct apr_bucket_defender *d = data;
-
- if (apr_bucket_shared_destroy(d))
- apr_bucket_free(d);
-}
-
-static apr_bucket *defender_bucket_make(apr_bucket *b,
- const struct buffer *buf)
-{
- struct apr_bucket_defender *d;
-
- d = apr_bucket_alloc(sizeof(*d), b->list);
-
- d->buf.area = buf->area;
- d->buf.data = buf->data;
- d->buf.size = 0;
-
- b = apr_bucket_shared_make(b, d, 0, buf->data);
- b->type = &apr_bucket_type_defender;
- return b;
-}
-
-static apr_bucket *defender_bucket_create(const struct buffer *buf,
- apr_bucket_alloc_t *list)
-{
- apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
-
- APR_BUCKET_INIT(b);
- b->free = apr_bucket_free;
- b->list = list;
- return defender_bucket_make(b, buf);
-}
-
-static void defender_logger(int level, char *str)
-{
- LOG(&null_worker, "%s", str);
-}
-
-static char *defender_strdup(apr_pool_t *pool, const char *src, uint64_t len)
-{
- char *dst;
-
- if (!(dst = apr_pcalloc(pool, len + 1)))
- return NULL;
-
- memcpy(dst, src, len);
- dst[len] = '\0';
-
- return dst;
-}
-
-static char *defender_printf(apr_pool_t *pool, const char *fmt, ...)
-{
- va_list argp;
- char *dst;
- int len;
-
- va_start(argp, fmt);
- len = vsnprintf(NULL, 0, fmt, argp);
- va_end(argp);
-
- if (len < 0)
- return NULL;
-
- if (!(dst = apr_pcalloc(pool, len + 1)))
- return NULL;
-
- va_start(argp, fmt);
- len = vsnprintf(dst, len + 1, fmt, argp);
- va_end(argp);
-
- return dst;
-}
-
-static char *defender_addr2str(apr_pool_t *pool, struct sample *addr)
-{
- sa_family_t family;
- const void *src;
- char *dst;
-
- switch (addr->data.type) {
- case SMP_T_IPV4:
- src = &addr->data.u.ipv4;
- family = AF_INET;
- break;
- case SMP_T_IPV6:
- src = &addr->data.u.ipv6;
- family = AF_INET6;
- break;
- default:
- return NULL;
- }
-
- if (!(dst = apr_pcalloc(pool, INET6_ADDRSTRLEN + 1)))
- return NULL;
-
- if (inet_ntop(family, src, dst, INET6_ADDRSTRLEN))
- return dst;
-
- return NULL;
-}
-
-static void defender_pre_config()
-{
- apr_pool_t *ptemp = NULL;
-
- defender_module.module_index = 0;
- defender_module.register_hooks(defender_pool);
-
- apr_pool_create(&ptemp, defender_pool);
- run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
- apr_pool_destroy(ptemp);
-}
-
-static const char *defender_read_config(const char *file)
-{
- apr_pool_t *ptemp = NULL;
- const char *err;
- const char *fullname;
-
- defender_module_config = defender_module.create_dir_config(defender_pool, "/");
- if (defender_module_config == NULL) {
- return "cannot allocate space for the configuration structure";
- }
-
- apr_pool_create(&ptemp, defender_pool);
-
- fullname = ap_server_root_relative(ptemp, file);
-
- err = read_module_config(server, defender_module_config,
- defender_module.cmds,
- defender_pool, ptemp, fullname);
-
- apr_pool_destroy(ptemp);
-
- return err;
-}
-
-static void defender_post_config()
-{
- apr_pool_t *ptemp = NULL;
-
- apr_pool_create(&ptemp, defender_pool);
- run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
- apr_pool_destroy(ptemp);
-}
-
-static const char *defender_set_logger(const char *file)
-{
- char *logname;
-
- logger = defender_logger;
-
- if (file == NULL)
- return NULL;
-
- logname = ap_server_root_relative(defender_pool, file);
-
- if (apr_file_open(&server->error_log, logname,
- APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
- APR_OS_DEFAULT, defender_pool) != APR_SUCCESS) {
- return apr_pstrcat(defender_pool, "Cannot open log file, ",
- logname, NULL);
- }
- server->error_fname = logname;
-
- return NULL;
-}
-
-static apr_status_t defender_input_filter(ap_filter_t *f,
- apr_bucket_brigade *new_bb,
- ap_input_mode_t mode,
- apr_read_type_e block,
- apr_off_t readbytes)
-{
- apr_bucket_brigade *bb = NULL;
- apr_bucket *b = NULL, *a = NULL;
- apr_status_t rv;
-
- bb = (apr_bucket_brigade *)apr_table_get(f->r->notes, DEFENDER_BRIGADE_REQUEST);
-
- if (bb == NULL || (bb && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)))) {
- b = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(new_bb, b);
- if (bb == NULL)
- return APR_SUCCESS;
- }
-
- rv = apr_brigade_partition(bb, readbytes, &a);
- if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
- return rv;
-
- b = APR_BRIGADE_FIRST(bb);
-
- while (b != a) {
- if (APR_BUCKET_IS_EOS(b))
- ap_remove_input_filter(f);
-
- APR_BUCKET_REMOVE(b);
- APR_BRIGADE_INSERT_TAIL(new_bb, b);
- b = APR_BRIGADE_FIRST(bb);
- }
-
- return APR_SUCCESS;
-}
-
-static conn_rec *defender_create_conn()
-{
- conn_rec *c = NULL;
- apr_pool_t *ptrans = NULL;
-
- apr_pool_create(&ptrans, defender_pool);
-
- c = apr_pcalloc(ptrans, sizeof(conn_rec));
-
- c->pool = ptrans;
- c->local_ip = "127.0.0.1";
- c->local_addr = server->addrs->host_addr;
- c->local_host = defender_name;
- c->client_addr = server->addrs->host_addr;
- c->remote_host = defender_name;
-
- c->id = 1;
- c->base_server = server;
- c->bucket_alloc = apr_bucket_alloc_create(ptrans);
-
- return c;
-}
-
-static request_rec *defender_create_request(conn_rec *conn)
-{
- request_rec *r = NULL;
- apr_pool_t *p = NULL;
- struct ap_logconf *l;
-
- apr_pool_create(&p, conn->pool);
-
- r = apr_pcalloc(p, sizeof(request_rec));
-
- r->pool = p;
- r->connection = conn;
- r->server = conn->base_server;
-
- r->headers_in = apr_table_make(p, 25);
- r->headers_out = apr_table_make(p, 12);
- r->subprocess_env = apr_table_make(p, 25);
- r->err_headers_out = apr_table_make(p, 5);
- r->notes = apr_table_make(p, 5);
-
- r->request_config = apr_palloc(p, sizeof(void *));
- r->per_dir_config = apr_palloc(p, sizeof(void *));
- ((void **)r->per_dir_config)[0] = defender_module_config;
-
- r->handler = defender_name;
-
- r->parsed_uri.scheme = "http";
- r->parsed_uri.is_initialized = 1;
- r->parsed_uri.port = 80;
- r->parsed_uri.port_str = "80";
- r->parsed_uri.fragment = "";
-
- r->input_filters = NULL;
- r->output_filters = NULL;
-
- l = apr_pcalloc(p, sizeof(struct ap_logconf));
- l->level = APLOG_DEBUG;
- r->log = l;
-
- return r;
-}
-
-static int defender_process_headers(request_rec *r)
-{
- return run_ap_hook_header_parser(r);
-}
-
-static int defender_process_body(request_rec *r)
-{
- ap_add_input_filter(DEFENDER_INPUT_FILTER, NULL, r, r->connection);
- return run_ap_hook_fixups(r);
-}
-
-int defender_init(const char *config_file, const char *log_file)
-{
- apr_status_t rv;
- const char *msg;
-
- if (!config_file) {
- LOG(&null_worker, "Mod Defender configuration file not specified.\n");
- return 0;
- }
-
- apr_initialize();
- apr_pool_create(&defender_pool, NULL);
- apr_hook_global_pool = defender_pool;
-
- ap_server_root = getcwd(defender_cwd, APR_PATH_MAX);
-
- server = (server_rec *) apr_palloc(defender_pool, sizeof(server_rec));
- server->process = apr_palloc(defender_pool, sizeof(process_rec));
- server->process->argc = 1;
- server->process->argv = defender_argv;
- server->process->short_name = defender_name;
- server->process->pconf = defender_pool;
- server->process->pool = defender_pool;
-
- server->addrs = apr_palloc(defender_pool, sizeof(server_addr_rec));
- rv = apr_sockaddr_info_get(&server->addrs->host_addr,
- "127.0.0.1", APR_UNSPEC, 0, 0,
- defender_pool);
- if (rv != APR_SUCCESS) {
- LOG(&null_worker, "Mod Defender getaddrinfo failed.\n");
- return 0;
- }
-
- server->path = "/";
- server->pathlen = strlen(server->path);
- server->port = 0;
- server->server_admin = defender_name;
- server->server_scheme = "";
- server->error_fname = NULL;
- server->error_log = NULL;
- server->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
- server->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
- server->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
- server->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
-
- memset(hostname, 0, sizeof(hostname));
- gethostname(hostname, sizeof(hostname) - 1);
- server->server_hostname = hostname;
-
- server->addrs->host_port = 0;
- server->names = server->wild_names = NULL;
- server->is_virtual = 0;
-
- server->lookup_defaults = NULL;
- server->module_config = NULL;
-
- msg = defender_set_logger(log_file);
- if (msg != NULL) {
- LOG(&null_worker, "Mod Defender init failed: %s\n", msg);
- return 0;
- }
-
- ap_register_input_filter(DEFENDER_INPUT_FILTER, defender_input_filter,
- NULL, AP_FTYPE_RESOURCE);
-
- defender_pre_config();
-
- msg = defender_read_config(config_file);
- if (msg != NULL) {
- LOG(&null_worker, "Mod Defender configuration failed: %s\n", msg);
- return 0;
- }
-
- defender_post_config();
-
- return 1;
-}
-
-int defender_process_request(struct worker *worker, struct defender_request *request)
-{
- struct conn_rec *c = NULL;
- struct request_rec *r = NULL;
-
- struct apr_bucket_brigade *bb = NULL;
- struct apr_bucket *d = NULL, *e = NULL;
-
- struct buffer *method;
- struct buffer *path;
- struct buffer *query;
- struct buffer *version;
- struct buffer *body;
-
- struct defender_header hdr;
- char *hdr_ptr, *hdr_end;
-
- const char *ptr;
-
- int status = DECLINED;
-
- if (!(c = defender_create_conn()))
- goto out;
-
- if (!(r = defender_create_request(c)))
- goto out;
-
- /* request */
- r->request_time = apr_time_now();
-
- if (request->clientip.data.type != SMP_T_IPV4 &&
- request->clientip.data.type != SMP_T_IPV6)
- goto out;
-
- if (!(r->useragent_ip = defender_addr2str(r->pool, &request->clientip)))
- goto out;
-
- if (request->id.data.u.str.area && request->id.data.u.str.data > 0) {
- apr_table_setn(r->subprocess_env, "UNIQUE_ID",
- defender_strdup(r->pool, request->id.data.u.str.area,
- request->id.data.u.str.data));
- }
- else {
- apr_table_setn(r->subprocess_env, "UNIQUE_ID",
- DEFENDER_DEFAULT_UNIQUE_ID);
- }
-
- method = &request->method.data.u.str;
- path = &request->path.data.u.str;
- query = &request->query.data.u.str;
- version = &request->version.data.u.str;
-
- r->method_number = lookup_builtin_method(method->area, method->data);
- if (!(r->method = defender_strdup(r->pool, method->area, method->data)))
- goto out;
-
- r->unparsed_uri = defender_printf(r->pool, "%.*s%s%.*s",
- path->data, path->area,
- query->data > 0 ? "?" : "",
- query->data, query->area);
- if (!r->unparsed_uri)
- goto out;
-
- if (!(r->uri = defender_strdup(r->pool, path->area, path->data)))
- goto out;
-
- r->parsed_uri.path = r->filename = r->uri;
-
- if (!(r->args = defender_strdup(r->pool, query->area, query->data)))
- goto out;
-
- r->parsed_uri.query = r->args;
-
- r->protocol = defender_printf(r->pool, "%s%.*s",
- version->data > 0 ? "HTTP/" : "",
- version->data, version->area);
- if (!r->protocol)
- goto out;
-
- r->the_request = defender_printf(r->pool, "%.*s %s%s%s",
- method->data, method->area,
- r->unparsed_uri,
- version->data > 0 ? " " : "",
- r->protocol);
- if (!r->the_request)
- goto out;
-
- /* headers */
- if (request->headers.data.type != SMP_T_BIN)
- goto misc;
-
- hdr_ptr = request->headers.data.u.str.area;
- hdr_end = hdr_ptr + request->headers.data.u.str.data;
-
- while (1) {
- memset(&hdr, 0, sizeof(hdr));
-
- if (decode_varint(&hdr_ptr, hdr_end, &hdr.name.len) == -1)
- goto out;
- if (!(hdr.name.str = defender_strdup(r->pool, hdr_ptr, hdr.name.len)))
- goto out;
-
- hdr_ptr += hdr.name.len;
- if (hdr_ptr > hdr_end)
- goto out;
-
- if (decode_varint(&hdr_ptr, hdr_end, &hdr.value.len) == -1)
- goto out;
- if (!(hdr.value.str = defender_strdup(r->pool, hdr_ptr, hdr.value.len)))
- goto out;
-
- hdr_ptr += hdr.value.len;
- if (hdr_ptr > hdr_end)
- goto out;
-
- if (!hdr.name.len && !hdr.value.len)
- break;
-
- apr_table_setn(r->headers_in, hdr.name.str, hdr.value.str);
- }
-
-misc:
-
- r->hostname = apr_table_get(r->headers_in, "Host");
- if (!r->hostname)
- r->hostname = defender_unknown_hostname;
- r->parsed_uri.hostname = (char *)r->hostname;
-
- r->content_type = apr_table_get(r->headers_in, "Content-Type");
- r->content_encoding = apr_table_get(r->headers_in, "Content-Encoding");
- ptr = apr_table_get(r->headers_in, "Content-Length");
- if (ptr)
- r->clength = strtol(ptr, NULL, 10);
-
- /* body */
- body = &request->body.data.u.str;
-
- bb = apr_brigade_create(r->pool, c->bucket_alloc);
- if (bb == NULL)
- goto out;
-
- d = defender_bucket_create(body, c->bucket_alloc);
- if (d == NULL)
- goto out;
-
- APR_BRIGADE_INSERT_TAIL(bb, d);
-
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
-
- apr_table_setn(r->notes, DEFENDER_BRIGADE_REQUEST, (char *)bb);
-
- /* process */
- status = defender_process_headers(r);
-
- if (status == DECLINED)
- status = defender_process_body(r);
-
- apr_brigade_cleanup(bb);
-
- /* success */
- if (status == DECLINED)
- status = OK;
-
-out:
-
- if (r && r->pool) {
- apr_table_clear(r->headers_in);
- apr_table_clear(r->headers_out);
- apr_table_clear(r->subprocess_env);
- apr_table_clear(r->err_headers_out);
- apr_table_clear(r->notes);
- apr_pool_destroy(r->pool);
- }
-
- if (c && c->pool) {
- apr_bucket_alloc_destroy(c->bucket_alloc);
- apr_pool_destroy(c->pool);
- }
-
- return status;
-}
+++ /dev/null
-/*
- * Mod Defender for HAProxy
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@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
- * 3 of the License, or (at your option) any later version.
- *
- */
-#ifndef __DEFENDER_H__
-#define __DEFENDER_H__
-
-#include <haproxy/sample-t.h>
-
-struct defender_request {
- struct sample clientip;
- struct sample id;
- struct sample method;
- struct sample path;
- struct sample query;
- struct sample version;
- struct sample headers;
- struct sample body;
-};
-
-struct defender_header {
- struct {
- char *str;
- uint64_t len;
- } name;
- struct {
- char *str;
- uint64_t len;
- } value;
-};
-
-int defender_init(const char *config_file, const char *log_file);
-int defender_process_request(struct worker *worker, struct defender_request *request);
-
-#endif /* __DEFENDER_H__ */
+++ /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/defaults.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/defaults.h
- * Miscellaneous default values.
- *
- * 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_DEFAULTS_H
-#define _HAPROXY_DEFAULTS_H
-
-/* MAX_PROCS defines the highest limit for the global "nbproc" value. It
- * defaults to the number of bits in a long integer but may be lowered to save
- * resources on embedded systems.
- */
-#ifndef MAX_PROCS
-#define MAX_PROCS LONGBITS
-#endif
-
-/* MAX_THREADS defines the highest limit for the global nbthread value. It
- * defaults to the number of bits in a long integer when threads are enabled
- * but may be lowered to save resources on embedded systems.
-*/
-#ifndef USE_THREAD
-/* threads disabled, 1 thread max */
-#define MAX_THREADS 1
-#define MAX_THREADS_MASK 1
-
-#else
-/* threads enabled, max_threads defaults to long bits */
-#ifndef MAX_THREADS
-#define MAX_THREADS LONGBITS
-#endif
-#define MAX_THREADS_MASK (~0UL >> (LONGBITS - MAX_THREADS))
-#endif
-
-/*
- * BUFSIZE defines the size of a read and write buffer. It is the maximum
- * amount of bytes which can be stored by the proxy for each stream. However,
- * when reading HTTP headers, the proxy needs some spare space to add or rewrite
- * headers if needed. The size of this spare is defined with MAXREWRITE. So it
- * is not possible to process headers longer than BUFSIZE-MAXREWRITE bytes. By
- * default, BUFSIZE=16384 bytes and MAXREWRITE=min(1024,BUFSIZE/2), so the
- * maximum length of headers accepted is 15360 bytes.
- */
-#ifndef BUFSIZE
-#define BUFSIZE 16384
-#endif
-
-/* certain buffers may only be allocated for responses in order to avoid
- * deadlocks caused by request queuing. 2 buffers is the absolute minimum
- * acceptable to ensure that a request gaining access to a server can get
- * a response buffer even if it doesn't completely flush the request buffer.
- * The worst case is an applet making use of a request buffer that cannot
- * completely be sent while the server starts to respond, and all unreserved
- * buffers are allocated by request buffers from pending connections in the
- * queue waiting for this one to flush. Both buffers reserved buffers may
- * thus be used at the same time.
- */
-#ifndef RESERVED_BUFS
-#define RESERVED_BUFS 2
-#endif
-
-// reserved buffer space for header rewriting
-#ifndef MAXREWRITE
-#define MAXREWRITE 1024
-#endif
-
-#ifndef REQURI_LEN
-#define REQURI_LEN 1024
-#endif
-
-#ifndef CAPTURE_LEN
-#define CAPTURE_LEN 64
-#endif
-
-#ifndef MAX_SYSLOG_LEN
-#define MAX_SYSLOG_LEN 1024
-#endif
-
-/* 64kB to archive startup-logs seems way more than enough */
-#ifndef STARTUP_LOG_SIZE
-#define STARTUP_LOG_SIZE 65536
-#endif
-
-// maximum line size when parsing config
-#ifndef LINESIZE
-#define LINESIZE 2048
-#endif
-
-// max # args on a configuration line
-#define MAX_LINE_ARGS 64
-
-// maximum line size when parsing crt-bind-list config
-#define CRT_LINESIZE 65536
-
-// max # args on crt-bind-list configuration line
-#define MAX_CRT_ARGS 2048
-
-// max # args on a command issued on the CLI ("stats socket")
-// This should cover at least 5 + twice the # of data_types
-#define MAX_CLI_ARGS 64
-
-// max # of matches per regexp
-#define MAX_MATCH 10
-
-// max # of headers in one HTTP request or response
-// By default, about 100 headers (+1 for the first line)
-#ifndef MAX_HTTP_HDR
-#define MAX_HTTP_HDR 101
-#endif
-
-// max # of headers in history when looking for header #-X
-#ifndef MAX_HDR_HISTORY
-#define MAX_HDR_HISTORY 10
-#endif
-
-// max # of stick counters per session (at least 3 for sc0..sc2)
-#ifndef MAX_SESS_STKCTR
-#define MAX_SESS_STKCTR 3
-#endif
-
-// max # of extra stick-table data types that can be registered at runtime
-#ifndef STKTABLE_EXTRA_DATA_TYPES
-#define STKTABLE_EXTRA_DATA_TYPES 0
-#endif
-
-// max # of stick-table filter entries that can be used during dump
-#ifndef STKTABLE_FILTER_LEN
-#define STKTABLE_FILTER_LEN 4
-#endif
-
-// max # of loops we can perform around a read() which succeeds.
-// It's very frequent that the system returns a few TCP segments at a time.
-#ifndef MAX_READ_POLL_LOOPS
-#define MAX_READ_POLL_LOOPS 4
-#endif
-
-// minimum number of bytes read at once above which we don't try to read
-// more, in order not to risk facing an EAGAIN. Most often, if we read
-// at least 10 kB, we can consider that the system has tried to read a
-// full buffer and got multiple segments (>1 MSS for jumbo frames, >7 MSS
-// for normal frames) did not bother truncating the last segment.
-#ifndef MIN_RECV_AT_ONCE_ENOUGH
-#define MIN_RECV_AT_ONCE_ENOUGH (7*1448)
-#endif
-
-// The minimum number of bytes to be forwarded that is worth trying to splice.
-// Below 4kB, it's not worth allocating pipes nor pretending to zero-copy.
-#ifndef MIN_SPLICE_FORWARD
-#define MIN_SPLICE_FORWARD 4096
-#endif
-
-// the max number of events returned in one call to poll/epoll. Too small a
-// value will cause lots of calls, and too high a value may cause high latency.
-#ifndef MAX_POLL_EVENTS
-#define MAX_POLL_EVENTS 200
-#endif
-
-// The maximum number of connections accepted at once by a thread for a single
-// listener. It used to default to 64 divided by the number of processes but
-// the tasklet-based model is much more scalable and benefits from smaller
-// values. Experimentation has shown that 4 gives the highest accept rate for
-// all thread values, and that 3 and 5 come very close, as shown below (HTTP/1
-// connections forwarded per second at multi-accept 4 and 64):
-//
-// ac\thr| 1 2 4 8 16
-// ------+------------------------------
-// 4| 80k 106k 168k 270k 336k
-// 64| 63k 89k 145k 230k 274k
-//
-#ifndef MAX_ACCEPT
-#define MAX_ACCEPT 4
-#endif
-
-// The base max number of tasks to run at once to be used when not set by
-// tune.runqueue-depth. It will automatically be divided by the square root
-// of the number of threads for better fairness. As such, 64 threads will
-// use 35 and a single thread will use 280.
-#ifndef RUNQUEUE_DEPTH
-#define RUNQUEUE_DEPTH 280
-#endif
-
-// cookie delimiter in "prefix" mode. This character is inserted between the
-// persistence cookie and the original value. The '~' is allowed by RFC6265,
-// and should not be too common in server names.
-#ifndef COOKIE_DELIM
-#define COOKIE_DELIM '~'
-#endif
-
-// this delimiter is used between a server's name and a last visit date in
-// cookies exchanged with the client.
-#ifndef COOKIE_DELIM_DATE
-#define COOKIE_DELIM_DATE '|'
-#endif
-
-#define CONN_RETRIES 3
-
-#define CHK_CONNTIME 2000
-#define DEF_CHKINTR 2000
-#define DEF_MAILALERTTIME 10000
-#define DEF_FALLTIME 3
-#define DEF_RISETIME 2
-#define DEF_AGENT_FALLTIME 1
-#define DEF_AGENT_RISETIME 1
-#define DEF_CHECK_PATH ""
-
-
-#define DEF_HANA_ONERR HANA_ONERR_FAILCHK
-#define DEF_HANA_ERRLIMIT 10
-
-// X-Forwarded-For header default
-#define DEF_XFORWARDFOR_HDR "X-Forwarded-For"
-
-// X-Original-To header default
-#define DEF_XORIGINALTO_HDR "X-Original-To"
-
-/* Default connections limit.
- *
- * A system limit can be enforced at build time in order to avoid using haproxy
- * beyond reasonable system limits. For this, just define SYSTEM_MAXCONN to the
- * absolute limit accepted by the system. If the configuration specifies a
- * higher value, it will be capped to SYSTEM_MAXCONN and a warning will be
- * emitted. The only way to override this limit will be to set it via the
- * command-line '-n' argument. If SYSTEM_MAXCONN is not set, a minimum value
- * of 100 will be used for DEFAULT_MAXCONN which almost guarantees that a
- * process will correctly start in any situation.
- */
-#ifdef SYSTEM_MAXCONN
-#undef DEFAULT_MAXCONN
-#define DEFAULT_MAXCONN SYSTEM_MAXCONN
-#elif !defined(DEFAULT_MAXCONN)
-#define DEFAULT_MAXCONN 100
-#endif
-
-/* Minimum check interval for spread health checks. Servers with intervals
- * greater than or equal to this value will have their checks spread apart
- * and will be considered when searching the minimal interval.
- * Others will be ignored for the minimal interval and will have their checks
- * scheduled on a different basis.
- */
-#ifndef SRV_CHK_INTER_THRES
-#define SRV_CHK_INTER_THRES 1000
-#endif
-
-/* Specifies the string used to report the version and release date on the
- * statistics page. May be defined to the empty string ("") to permanently
- * disable the feature.
- */
-#ifndef STATS_VERSION_STRING
-#define STATS_VERSION_STRING " version " HAPROXY_VERSION ", released " HAPROXY_DATE
-#endif
-
-/* This is the default statistics URI */
-#ifdef CONFIG_STATS_DEFAULT_URI
-#define STATS_DEFAULT_URI CONFIG_STATS_DEFAULT_URI
-#else
-#define STATS_DEFAULT_URI "/haproxy?stats"
-#endif
-
-/* This is the default statistics realm */
-#ifdef CONFIG_STATS_DEFAULT_REALM
-#define STATS_DEFAULT_REALM CONFIG_STATS_DEFAULT_REALM
-#else
-#define STATS_DEFAULT_REALM "HAProxy Statistics"
-#endif
-
-/* Maximum signal queue size, and also number of different signals we can
- * handle.
- */
-#ifndef MAX_SIGNAL
-#define MAX_SIGNAL 256
-#endif
-
-/* Maximum host name length */
-#ifndef MAX_HOSTNAME_LEN
-#if MAXHOSTNAMELEN
-#define MAX_HOSTNAME_LEN MAXHOSTNAMELEN
-#else
-#define MAX_HOSTNAME_LEN 64
-#endif // MAXHOSTNAMELEN
-#endif // MAX_HOSTNAME_LEN
-
-/* Maximum health check description length */
-#ifndef HCHK_DESC_LEN
-#define HCHK_DESC_LEN 128
-#endif
-
-/* ciphers used as defaults on connect */
-#ifndef CONNECT_DEFAULT_CIPHERS
-#define CONNECT_DEFAULT_CIPHERS NULL
-#endif
-
-/* ciphers used as defaults on TLS 1.3 connect */
-#ifndef CONNECT_DEFAULT_CIPHERSUITES
-#define CONNECT_DEFAULT_CIPHERSUITES NULL
-#endif
-
-/* ciphers used as defaults on listeners */
-#ifndef LISTEN_DEFAULT_CIPHERS
-#define LISTEN_DEFAULT_CIPHERS NULL
-#endif
-
-/* cipher suites used as defaults on TLS 1.3 listeners */
-#ifndef LISTEN_DEFAULT_CIPHERSUITES
-#define LISTEN_DEFAULT_CIPHERSUITES NULL
-#endif
-
-/* named curve used as defaults for ECDHE ciphers */
-#ifndef ECDHE_DEFAULT_CURVE
-#define ECDHE_DEFAULT_CURVE "prime256v1"
-#endif
-
-/* ssl cache size */
-#ifndef SSLCACHESIZE
-#define SSLCACHESIZE 20000
-#endif
-
-/* ssl max dh param size */
-#ifndef SSL_DEFAULT_DH_PARAM
-#define SSL_DEFAULT_DH_PARAM 0
-#endif
-
-/* max memory cost per SSL session */
-#ifndef SSL_SESSION_MAX_COST
-#define SSL_SESSION_MAX_COST (16*1024) // measured
-#endif
-
-/* max memory cost per SSL handshake (on top of session) */
-#ifndef SSL_HANDSHAKE_MAX_COST
-#define SSL_HANDSHAKE_MAX_COST (76*1024) // measured
-#endif
-
-#ifndef DEFAULT_SSL_CTX_CACHE
-#define DEFAULT_SSL_CTX_CACHE 1000
-#endif
-
-/* approximate stream size (for maxconn estimate) */
-#ifndef STREAM_MAX_COST
-#define STREAM_MAX_COST (sizeof(struct stream) + \
- 2 * sizeof(struct channel) + \
- 2 * sizeof(struct connection) + \
- global.tune.requri_len + \
- 2 * global.tune.cookie_len)
-#endif
-
-/* available memory estimate : count about 3% of overhead in various structures */
-#ifndef MEM_USABLE_RATIO
-#define MEM_USABLE_RATIO 0.97
-#endif
-
-/* default per-thread pool cache size when enabled */
-#ifndef CONFIG_HAP_POOL_CACHE_SIZE
-#define CONFIG_HAP_POOL_CACHE_SIZE 1048576
-#endif
-
-/* Number of samples used to compute the times reported in stats. A power of
- * two is highly recommended, and this value multiplied by the largest response
- * time must not overflow and unsigned int. See freq_ctr.h for more information.
- * We consider that values are accurate to 95% with two batches of samples below,
- * so in order to advertise accurate times across 1k samples, we effectively
- * measure over 512.
- */
-#ifndef TIME_STATS_SAMPLES
-#define TIME_STATS_SAMPLES 512
-#endif
-
-/* max ocsp cert id asn1 encoded length */
-#ifndef OCSP_MAX_CERTID_ASN1_LENGTH
-#define OCSP_MAX_CERTID_ASN1_LENGTH 128
-#endif
-
-#ifndef OCSP_MAX_RESPONSE_TIME_SKEW
-#define OCSP_MAX_RESPONSE_TIME_SKEW 300
-#endif
-
-/* Number of TLS tickets to check, used for rotation */
-#ifndef TLS_TICKETS_NO
-#define TLS_TICKETS_NO 3
-#endif
-
-/* pattern lookup default cache size, in number of entries :
- * 10k entries at 10k req/s mean 1% risk of a collision after 60 years, that's
- * already much less than the memory's reliability in most machines and more
- * durable than most admin's life expectancy. A collision will result in a
- * valid result to be returned for a different entry from the same list.
- */
-#ifndef DEFAULT_PAT_LRU_SIZE
-#define DEFAULT_PAT_LRU_SIZE 10000
-#endif
-
-/* maximum number of pollers that may be registered */
-#ifndef MAX_POLLERS
-#define MAX_POLLERS 10
-#endif
-
-/* Make all xxhash functions inline, with implementations being directly
- * included within xxhash.h.
- */
-#ifndef XXH_INLINE_ALL
-#define XXH_INLINE_ALL
-#endif
-
-#endif /* _HAPROXY_DEFAULTS_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
-/*
- * include/haproxy/tools.h
- * This files contains some general purpose functions and macros.
- *
- * 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_TOOLS_H
-#define _HAPROXY_TOOLS_H
-
-#include <string.h>
-#include <stdio.h>
-#include <time.h>
-#include <stdarg.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-/****** string-specific macros and functions ******/
-/* if a > max, then bound <a> to <max>. The macro returns the new <a> */
-#define UBOUND(a, max) ({ typeof(a) b = (max); if ((a) > b) (a) = b; (a); })
-
-/* if a < min, then bound <a> to <min>. The macro returns the new <a> */
-#define LBOUND(a, min) ({ typeof(a) b = (min); if ((a) < b) (a) = b; (a); })
-
-#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0)
-
-/* returns true if <c> is an identifier character, that is, a digit, a letter,
- * or '-', '+', '_', ':' or '.'. This is usable for proxy names, server names,
- * ACL names, sample fetch names, and converter names.
- */
-static inline char *cut_crlf(char *s) {
-
- while (*s != '\r' && *s != '\n') {
- char *p = s++;
-
- if (!*p)
- return p;
- }
-
- *s++ = '\0';
-
- return s;
-}
-
-static inline char *ltrim(char *s, char c) {
-
- if (c)
- while (*s == c)
- s++;
-
- return s;
-}
-
-static inline char *rtrim(char *s, char c) {
-
- char *p = s + strlen(s);
-
- while (p-- > s)
- if (*p == c)
- *p = '\0';
- else
- break;
-
- return s;
-}
-
-static inline char *alltrim(char *s, char c) {
-
- rtrim(s, c);
-
- return ltrim(s, c);
-}
-
-/* This function converts the time_t value <now> into a broken out struct tm
- * which must be allocated by the caller. It is highly recommended to use this
- * function instead of localtime() because that one requires a time_t* which
- * is not always compatible with tv_sec depending on OS/hardware combinations.
- */
-static inline void get_localtime(const time_t now, struct tm *tm)
-{
- localtime_r(&now, tm);
-}
-
-/* This function converts the time_t value <now> into a broken out struct tm
- * which must be allocated by the caller. It is highly recommended to use this
- * function instead of gmtime() because that one requires a time_t* which
- * is not always compatible with tv_sec depending on OS/hardware combinations.
- */
-static inline void get_gmtime(const time_t now, struct tm *tm)
-{
- gmtime_r(&now, tm);
-}
-
-/* Counts a number of elapsed days since 01/01/0000 based solely on elapsed
- * years and assuming the regular rule for leap years applies. It's fake but
- * serves as a temporary origin. It's worth remembering that it's the first
- * year of each period that is leap and not the last one, so for instance year
- * 1 sees 366 days since year 0 was leap. For this reason we have to apply
- * modular arithmetic which is why we offset the year by 399 before
- * subtracting the excess at the end. No overflow here before ~11.7 million
- * years.
- */
-static inline unsigned int days_since_zero(unsigned int y)
-{
- return y * 365 + (y + 399) / 4 - (y + 399) / 100 + (y + 399) / 400
- - 399 / 4 + 399 / 100;
-}
-
-/* sets the address family to AF_UNSPEC so that is_addr() does not match */
-static inline void clear_addr(struct sockaddr_storage *addr)
-{
- addr->ss_family = AF_UNSPEC;
-}
-
-/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address,
- * otherwise zero.
- */
-static inline int is_inet_addr(const struct sockaddr_storage *addr)
-{
- int i;
-
- switch (addr->ss_family) {
- case AF_INET:
- return *(int *)&((struct sockaddr_in *)addr)->sin_addr;
- case AF_INET6:
- for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++)
- if (((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i] != 0)
- return ((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i];
- }
- return 0;
-}
-
-/* returns port in network byte order */
-static inline int get_net_port(struct sockaddr_storage *addr)
-{
- switch (addr->ss_family) {
- case AF_INET:
- return ((struct sockaddr_in *)addr)->sin_port;
- case AF_INET6:
- return ((struct sockaddr_in6 *)addr)->sin6_port;
- }
- return 0;
-}
-
-/* returns port in host byte order */
-static inline int get_host_port(struct sockaddr_storage *addr)
-{
- switch (addr->ss_family) {
- case AF_INET:
- return ntohs(((struct sockaddr_in *)addr)->sin_port);
- case AF_INET6:
- return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
- }
- return 0;
-}
-
-/* returns address len for <addr>'s family, 0 for unknown families */
-static inline int get_addr_len(const struct sockaddr_storage *addr)
-{
- switch (addr->ss_family) {
- case AF_INET:
- return sizeof(struct sockaddr_in);
- case AF_INET6:
- return sizeof(struct sockaddr_in6);
- case AF_UNIX:
- return sizeof(struct sockaddr_un);
- }
- return 0;
-}
-
-/* set port in host byte order */
-static inline int set_net_port(struct sockaddr_storage *addr, int port)
-{
- switch (addr->ss_family) {
- case AF_INET:
- ((struct sockaddr_in *)addr)->sin_port = port;
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *)addr)->sin6_port = port;
- break;
- }
- return 0;
-}
-
-/* set port in network byte order */
-static inline int set_host_port(struct sockaddr_storage *addr, int port)
-{
- switch (addr->ss_family) {
- case AF_INET:
- ((struct sockaddr_in *)addr)->sin_port = htons(port);
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
- break;
- }
- return 0;
-}
-
-/* after increasing a pointer value, it can exceed the first buffer
- * size. This function transform the value of <ptr> according with
- * the expected position. <chunks> is an array of the one or two
- * available chunks. The first value is the start of the first chunk,
- * the second value if the end+1 of the first chunks. The third value
- * is NULL or the start of the second chunk and the fourth value is
- * the end+1 of the second chunk. The function returns 1 if does a
- * wrap, else returns 0.
- */
-static inline int fix_pointer_if_wrap(const char **chunks, const char **ptr)
-{
- if (*ptr < chunks[1])
- return 0;
- if (!chunks[2])
- return 0;
- *ptr = chunks[2] + ( *ptr - chunks[1] );
- return 1;
-}
-
-/************************* Composite address manipulation *********************
- * Composite addresses are simply unsigned long data in which the higher bits
- * represent a pointer, and the two lower bits are flags. There are several
- * places where we just want to associate one or two flags to a pointer (eg,
- * to type it), and these functions permit this. The pointer is necessarily a
- * 32-bit aligned pointer, as its two lower bits will be cleared and replaced
- * with the flags.
- *****************************************************************************/
-
-/* Masks the two lower bits of a composite address and converts it to a
- * pointer. This is used to mix some bits with some aligned pointers to
- * structs and to retrieve the original (32-bit aligned) pointer.
- */
-static inline void *caddr_to_ptr(unsigned long caddr)
-{
- return (void *)(caddr & ~3UL);
-}
-
-/* Only retrieves the two lower bits of a composite address. This is used to mix
- * some bits with some aligned pointers to structs and to retrieve the original
- * data (2 bits).
- */
-static inline unsigned int caddr_to_data(unsigned long caddr)
-{
- return (caddr & 3UL);
-}
-
-/* Combines the aligned pointer whose 2 lower bits will be masked with the bits
- * from <data> to form a composite address. This is used to mix some bits with
- * some aligned pointers to structs and to retrieve the original (32-bit aligned)
- * pointer.
- */
-static inline unsigned long caddr_from_ptr(void *ptr, unsigned int data)
-{
- return (((unsigned long)ptr) & ~3UL) + (data & 3);
-}
-
-/* sets the 2 bits of <data> in the <caddr> composite address */
-static inline unsigned long caddr_set_flags(unsigned long caddr, unsigned int data)
-{
- return caddr | (data & 3);
-}
-
-/* clears the 2 bits of <data> in the <caddr> composite address */
-static inline unsigned long caddr_clr_flags(unsigned long caddr, unsigned int data)
-{
- return caddr & ~(unsigned long)(data & 3);
-}
-
-static inline unsigned char utf8_return_code(unsigned int code)
-{
- return code & 0xf0;
-}
-
-static inline unsigned char utf8_return_length(unsigned char code)
-{
- return code & 0x0f;
-}
-
-/* returns a 64-bit a timestamp with the finest resolution available. The
- * unit is intentionally not specified. It's mostly used to compare dates.
- */
-#if defined(__i386__) || defined(__x86_64__)
-static inline unsigned long long rdtsc()
-{
- unsigned int a, d;
- asm volatile("rdtsc" : "=a" (a), "=d" (d));
- return a + ((unsigned long long)d << 32);
-}
-#else
-static inline unsigned long long rdtsc()
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000000 + tv.tv_usec;
-}
-#endif
-
-/* Note that this may result in opening libgcc() on first call, so it may need
- * to have been called once before chrooting.
- */
-static forceinline int my_backtrace(void **buffer, int max)
-{
-#if !defined(USE_BACKTRACE)
- return 0;
-#elif defined(HA_HAVE_WORKING_BACKTRACE)
- return backtrace(buffer, max);
-#else
- const struct frame {
- const struct frame *next;
- void *ra;
- } *frame;
- int count;
-
- frame = __builtin_frame_address(0);
- for (count = 0; count < max && may_access(frame) && may_access(frame->ra);) {
- buffer[count++] = frame->ra;
- frame = frame->next;
- }
- return count;
-#endif
-}
-
-/* same as realloc() except that ptr is also freed upon failure */
-static inline void *my_realloc2(void *ptr, size_t size)
-{
- void *ret;
-
- ret = realloc(ptr, size);
- if (!ret && size)
- free(ptr);
- return ret;
-}
-
-#endif /* _HAPROXY_TOOLS_H */
+++ /dev/null
-/*
- * Mod Defender for HAProxy
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
- *
- * Based on "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
- * 3 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 "defender.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 defender_status; /* mod_defender returned status */
-
- 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->defender_status = -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 defender_request request;
-
- memset(&request, 0, sizeof(request));
-
- if (nbargs != 8)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.clientip) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.id) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.method) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.path) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.query) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.version) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.headers) == -1)
- goto skip_message;
-
- if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
- goto stop_processing;
- if (spoe_decode_data(&p, end, &request.body) == -1)
- goto skip_message;
-
- frame->defender_status = defender_process_request(frame->worker, &request);
- }
- 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->defender_status != -1) {
- DEBUG(frame->worker, "Add action : set variable status=%u",
- frame->defender_status);
-
- *p++ = SPOE_ACT_T_SET_VAR; /* Action type */
- *p++ = 3; /* Number of args */
- *p++ = SPOE_SCOPE_SESS; /* Arg 1: the scope */
- spoe_encode_buffer("status", 6, &p, end); /* Arg 2: variable name */
- *p++ = SPOE_DATA_T_UINT32;
- encode_varint(frame->defender_status, &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"
- " -f <config-file> Mod Defender configuration file\n"
- " -l <log-file> Mod Defender log file\n"
- " -d Enable the debug mode\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 *config_file = NULL;
- const char *log_file = NULL;
-
- // TODO: add '-t <processing-time>' option
- while ((opt = getopt(argc, argv, "hf:l:dm:n:p:c:t:")) != -1) {
- switch (opt) {
- case 'h':
- usage(argv[0]);
- return EXIT_SUCCESS;
- case 'f':
- config_file = optarg;
- break;
- case 'l':
- log_file = optarg;
- break;
- 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 '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 (!defender_init(config_file, log_file))
- 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
-/*
- * Mod Defender for HAProxy
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
- *
- * Based on "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
- * 3 of the License, or (at your option) any later version.
- *
- */
-#ifndef __SPOA_H__
-#define __SPOA_H__
-
-#include <sys/time.h>
-#undef LIST_HEAD
-
-#include <event2/util.h>
-#include <event2/event.h>
-#include <event2/event_struct.h>
-#include <event2/thread.h>
-
-#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)
-
-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;
-};
-
-extern struct worker null_worker;
-
-#endif /* __SPOA_H__ */
+++ /dev/null
-/*
- * Mod Defender for HAProxy
- *
- * Support for the Mod Defender code on non-Apache platforms.
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
- *
- * Parts of code based on Apache HTTP Server source
- * Copyright 2015 The Apache Software Foundation (http://www.apache.org/)
- *
- * 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
- * 3 of the License, or (at your option) any later version.
- *
- */
-#include <limits.h>
-
-#include <http_core.h>
-#include <http_main.h>
-#include <http_log.h>
-
-#include <apr_lib.h>
-#include <apr_strings.h>
-#include <apr_fnmatch.h>
-
-#include "standalone.h"
-
-#define MAX_ARGC 64
-#define MAX_INCLUDE_DIR_DEPTH 128
-
-#define SLASHES "/"
-
-#define FILTER_POOL apr_hook_global_pool
-#define TRIE_INITIAL_SIZE 4
-
-typedef struct filter_trie_node filter_trie_node;
-
-typedef struct {
- int c;
- filter_trie_node *child;
-} filter_trie_child_ptr;
-
-struct filter_trie_node {
- ap_filter_rec_t *frec;
- filter_trie_child_ptr *children;
- int nchildren;
- int size;
-};
-
-typedef struct {
- const char *fname;
-} fnames;
-
-AP_DECLARE_DATA const char *ap_server_root = "/";
-
-void (*logger)(int level, char *str) = NULL;
-
-static void str_tolower(char *str)
-{
- while (*str) {
- *str = apr_tolower(*str);
- ++str;
- }
-}
-
-static char x2c(const char *what)
-{
- char digit;
-
-#if !APR_CHARSET_EBCDIC
- digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
- : (what[0] - '0'));
- digit *= 16;
- digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
- : (what[1] - '0'));
-#else /*APR_CHARSET_EBCDIC*/
- char xstr[5];
- xstr[0]='0';
- xstr[1]='x';
- xstr[2]=what[0];
- xstr[3]=what[1];
- xstr[4]='\0';
- digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
- 0xFF & strtol(xstr, NULL, 16));
-#endif /*APR_CHARSET_EBCDIC*/
- return (digit);
-}
-
-static int unescape_url(char *url, const char *forbid, const char *reserved)
-{
- int badesc, badpath;
- char *x, *y;
-
- badesc = 0;
- badpath = 0;
- /* Initial scan for first '%'. Don't bother writing values before
- * seeing a '%' */
- y = strchr(url, '%');
- if (y == NULL) {
- return OK;
- }
- for (x = y; *y; ++x, ++y) {
- if (*y != '%') {
- *x = *y;
- }
- else {
- if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
- badesc = 1;
- *x = '%';
- }
- else {
- char decoded;
- decoded = x2c(y + 1);
- if ((decoded == '\0')
- || (forbid && ap_strchr_c(forbid, decoded))) {
- badpath = 1;
- *x = decoded;
- y += 2;
- }
- else if (reserved && ap_strchr_c(reserved, decoded)) {
- *x++ = *y++;
- *x++ = *y++;
- *x = *y;
- }
- else {
- *x = decoded;
- y += 2;
- }
- }
- }
- }
- *x = '\0';
- if (badesc) {
- return HTTP_BAD_REQUEST;
- }
- else if (badpath) {
- return HTTP_NOT_FOUND;
- }
- else {
- return OK;
- }
-}
-
-AP_DECLARE(int) ap_unescape_url(char *url)
-{
- /* Traditional */
- return unescape_url(url, SLASHES, NULL);
-}
-
-AP_DECLARE(void) ap_get_server_revision(ap_version_t *version)
-{
- version->major = AP_SERVER_MAJORVERSION_NUMBER;
- version->minor = AP_SERVER_MINORVERSION_NUMBER;
- version->patch = AP_SERVER_PATCHLEVEL_NUMBER;
- version->add_string = AP_SERVER_ADD_STRING;
-}
-
-static void log_error_core(const char *file, int line, int module_index,
- int level,
- apr_status_t status, const server_rec *s,
- const conn_rec *c,
- const request_rec *r, apr_pool_t *pool,
- const char *fmt, va_list args)
-{
- char errstr[MAX_STRING_LEN];
-
- apr_vsnprintf(errstr, MAX_STRING_LEN, fmt, args);
-
- if (logger != NULL)
- logger(level, errstr);
-}
-
-AP_DECLARE(void) ap_log_error_(const char *file, int line, int module_index,
- int level, apr_status_t status,
- const server_rec *s, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- log_error_core(file, line, module_index, level, status, s, NULL, NULL,
- NULL, fmt, args);
- va_end(args);
-}
-
-AP_DECLARE(void) ap_log_rerror_(const char *file, int line, int module_index,
- int level, apr_status_t status,
- const request_rec *r, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- log_error_core(file, line, module_index, level, status, r->server, NULL, r,
- NULL, fmt, args);
- va_end(args);
-}
-
-AP_DECLARE(void) ap_log_cerror_(const char *file, int line, int module_index,
- int level, apr_status_t status,
- const conn_rec *c, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- log_error_core(file, line, module_index, level, status, c->base_server, c,
- NULL, NULL, fmt, args);
- va_end(args);
-}
-
-AP_DECLARE(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
-{
- return NULL;
-}
-
-AP_DECLARE(apr_file_t *) ap_piped_log_write_fd(piped_log *pl)
-{
- return NULL;
-}
-
-static cmd_parms default_parms =
-{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
-
-AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file)
-{
- char *newpath = NULL;
- apr_status_t rv;
- rv = apr_filepath_merge(&newpath, ap_server_root, file,
- APR_FILEPATH_TRUENAME, p);
- if (newpath && (rv == APR_SUCCESS || APR_STATUS_IS_EPATHWILD(rv)
- || APR_STATUS_IS_ENOENT(rv)
- || APR_STATUS_IS_ENOTDIR(rv))) {
- return newpath;
- }
- else {
- return NULL;
- }
-}
-
-AP_DECLARE(apr_status_t) ap_get_brigade(ap_filter_t *next,
- apr_bucket_brigade *bb,
- ap_input_mode_t mode,
- apr_read_type_e block,
- apr_off_t readbytes)
-{
- if (next) {
- return next->frec->filter_func.in_func(next, bb, mode, block,
- readbytes);
- }
- return AP_NOBODY_READ;
-}
-
-static void
-argstr_to_table(char *str, apr_table_t *parms)
-{
- char *key;
- char *value;
- char *strtok_state;
-
- if (str == NULL) {
- return;
- }
-
- key = apr_strtok(str, "&", &strtok_state);
- while (key) {
- value = strchr(key, '=');
- if (value) {
- *value = '\0'; /* Split the string in two */
- value++; /* Skip passed the = */
- }
- else {
- value = "1";
- }
- ap_unescape_url(key);
- ap_unescape_url(value);
- apr_table_set(parms, key, value);
- key = apr_strtok(NULL, "&", &strtok_state);
- }
-}
-
-AP_DECLARE(void) ap_args_to_table(request_rec *r, apr_table_t **table)
-{
- apr_table_t *t = apr_table_make(r->pool, 10);
- argstr_to_table(apr_pstrdup(r->pool, r->args), t);
- *table = t;
-}
-
-/* Link a trie node to its parent
- */
-static void trie_node_link(apr_pool_t *p, filter_trie_node *parent,
- filter_trie_node *child, int c)
-{
- int i, j;
-
- if (parent->nchildren == parent->size) {
- filter_trie_child_ptr *new;
- parent->size *= 2;
- new = (filter_trie_child_ptr *)apr_palloc(p, parent->size *
- sizeof(filter_trie_child_ptr));
- memcpy(new, parent->children, parent->nchildren *
- sizeof(filter_trie_child_ptr));
- parent->children = new;
- }
-
- for (i = 0; i < parent->nchildren; i++) {
- if (c == parent->children[i].c) {
- return;
- }
- else if (c < parent->children[i].c) {
- break;
- }
- }
- for (j = parent->nchildren; j > i; j--) {
- parent->children[j].c = parent->children[j - 1].c;
- parent->children[j].child = parent->children[j - 1].child;
- }
- parent->children[i].c = c;
- parent->children[i].child = child;
-
- parent->nchildren++;
-}
-
-/* Allocate a new node for a trie.
- * If parent is non-NULL, link the new node under the parent node with
- * key 'c' (or, if an existing child node matches, return that one)
- */
-static filter_trie_node *trie_node_alloc(apr_pool_t *p,
- filter_trie_node *parent, char c)
-{
- filter_trie_node *new_node;
- if (parent) {
- int i;
- for (i = 0; i < parent->nchildren; i++) {
- if (c == parent->children[i].c) {
- return parent->children[i].child;
- }
- else if (c < parent->children[i].c) {
- break;
- }
- }
- new_node = (filter_trie_node *)apr_palloc(p, sizeof(filter_trie_node));
- trie_node_link(p, parent, new_node, c);
- }
- else { /* No parent node */
- new_node = (filter_trie_node *)apr_palloc(p,
- sizeof(filter_trie_node));
- }
-
- new_node->frec = NULL;
- new_node->nchildren = 0;
- new_node->size = TRIE_INITIAL_SIZE;
- new_node->children = (filter_trie_child_ptr *)apr_palloc(p,
- new_node->size * sizeof(filter_trie_child_ptr));
- return new_node;
-}
-
-static filter_trie_node *registered_output_filters = NULL;
-static filter_trie_node *registered_input_filters = NULL;
-
-
-static apr_status_t filter_cleanup(void *ctx)
-{
- registered_output_filters = NULL;
- registered_input_filters = NULL;
- return APR_SUCCESS;
-}
-
-static ap_filter_rec_t *register_filter(const char *name,
- ap_filter_func filter_func,
- ap_init_filter_func filter_init,
- ap_filter_type ftype,
- filter_trie_node **reg_filter_set)
-{
- ap_filter_rec_t *frec;
- char *normalized_name;
- const char *n;
- filter_trie_node *node;
-
- if (!*reg_filter_set) {
- *reg_filter_set = trie_node_alloc(FILTER_POOL, NULL, 0);
- }
-
- normalized_name = apr_pstrdup(FILTER_POOL, name);
- str_tolower(normalized_name);
-
- node = *reg_filter_set;
- for (n = normalized_name; *n; n++) {
- filter_trie_node *child = trie_node_alloc(FILTER_POOL, node, *n);
- if (apr_isalpha(*n)) {
- trie_node_link(FILTER_POOL, node, child, apr_toupper(*n));
- }
- node = child;
- }
- if (node->frec) {
- frec = node->frec;
- }
- else {
- frec = apr_pcalloc(FILTER_POOL, sizeof(*frec));
- node->frec = frec;
- frec->name = normalized_name;
- }
- frec->filter_func = filter_func;
- frec->filter_init_func = filter_init;
- frec->ftype = ftype;
-
- apr_pool_cleanup_register(FILTER_POOL, NULL, filter_cleanup,
- apr_pool_cleanup_null);
- return frec;
-}
-
-AP_DECLARE(ap_filter_rec_t *) ap_register_input_filter(const char *name,
- ap_in_filter_func filter_func,
- ap_init_filter_func filter_init,
- ap_filter_type ftype)
-{
- ap_filter_func f;
- f.in_func = filter_func;
- return register_filter(name, f, filter_init, ftype,
- ®istered_input_filters);
-}
-
-static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
- request_rec *r, conn_rec *c,
- ap_filter_t **r_filters,
- ap_filter_t **p_filters,
- ap_filter_t **c_filters)
-{
- apr_pool_t *p = frec->ftype < AP_FTYPE_CONNECTION && r ? r->pool : c->pool;
- ap_filter_t *f = apr_palloc(p, sizeof(*f));
- ap_filter_t **outf;
-
- if (frec->ftype < AP_FTYPE_PROTOCOL) {
- if (r) {
- outf = r_filters;
- }
- else {
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00080)
- "a content filter was added without a request: %s", frec->name);
- return NULL;
- }
- }
- else if (frec->ftype < AP_FTYPE_CONNECTION) {
- if (r) {
- outf = p_filters;
- }
- else {
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00081)
- "a protocol filter was added without a request: %s", frec->name);
- return NULL;
- }
- }
- else {
- outf = c_filters;
- }
-
- f->frec = frec;
- f->ctx = ctx;
- /* f->r must always be NULL for connection filters */
- f->r = frec->ftype < AP_FTYPE_CONNECTION ? r : NULL;
- f->c = c;
- f->next = NULL;
-
- if (INSERT_BEFORE(f, *outf)) {
- f->next = *outf;
-
- if (*outf) {
- ap_filter_t *first = NULL;
-
- if (r) {
- /* If we are adding our first non-connection filter,
- * Then don't try to find the right location, it is
- * automatically first.
- */
- if (*r_filters != *c_filters) {
- first = *r_filters;
- while (first && (first->next != (*outf))) {
- first = first->next;
- }
- }
- }
- if (first && first != (*outf)) {
- first->next = f;
- }
- }
- *outf = f;
- }
- else {
- ap_filter_t *fscan = *outf;
- while (!INSERT_BEFORE(f, fscan->next))
- fscan = fscan->next;
-
- f->next = fscan->next;
- fscan->next = f;
- }
-
- if (frec->ftype < AP_FTYPE_CONNECTION && (*r_filters == *c_filters)) {
- *r_filters = *p_filters;
- }
- return f;
-}
-
-static ap_filter_t *add_any_filter(const char *name, void *ctx,
- request_rec *r, conn_rec *c,
- const filter_trie_node *reg_filter_set,
- ap_filter_t **r_filters,
- ap_filter_t **p_filters,
- ap_filter_t **c_filters)
-{
- if (reg_filter_set) {
- const char *n;
- const filter_trie_node *node;
-
- node = reg_filter_set;
- for (n = name; *n; n++) {
- int start, end;
- start = 0;
- end = node->nchildren - 1;
- while (end >= start) {
- int middle = (end + start) / 2;
- char ch = node->children[middle].c;
- if (*n == ch) {
- node = node->children[middle].child;
- break;
- }
- else if (*n < ch) {
- end = middle - 1;
- }
- else {
- start = middle + 1;
- }
- }
- if (end < start) {
- node = NULL;
- break;
- }
- }
-
- if (node && node->frec) {
- return add_any_filter_handle(node->frec, ctx, r, c, r_filters,
- p_filters, c_filters);
- }
- }
-
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, r ? r->connection : c, APLOGNO(00082)
- "an unknown filter was not added: %s", name);
- return NULL;
-}
-
-AP_DECLARE(ap_filter_t *) ap_add_input_filter(const char *name, void *ctx,
- request_rec *r, conn_rec *c)
-{
- return add_any_filter(name, ctx, r, c, registered_input_filters,
- r ? &r->input_filters : NULL,
- r ? &r->proto_input_filters : NULL,
- &c->input_filters);
-}
-
-static void remove_any_filter(ap_filter_t *f, ap_filter_t **r_filt, ap_filter_t **p_filt,
- ap_filter_t **c_filt)
-{
- ap_filter_t **curr = r_filt ? r_filt : c_filt;
- ap_filter_t *fscan = *curr;
-
- if (p_filt && *p_filt == f)
- *p_filt = (*p_filt)->next;
-
- if (*curr == f) {
- *curr = (*curr)->next;
- return;
- }
-
- while (fscan->next != f) {
- if (!(fscan = fscan->next)) {
- return;
- }
- }
-
- fscan->next = f->next;
-}
-
-AP_DECLARE(void) ap_remove_input_filter(ap_filter_t *f)
-{
- remove_any_filter(f, f->r ? &f->r->input_filters : NULL,
- f->r ? &f->r->proto_input_filters : NULL,
- &f->c->input_filters);
-}
-
-static int cfg_closefile(ap_configfile_t *cfp)
-{
-#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "Done with config file %s", cfp->name);
-#endif
- return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
-}
-
-/* we can't use apr_file_* directly because of linking issues on Windows */
-static apr_status_t cfg_close(void *param)
-{
- return apr_file_close(param);
-}
-
-static apr_status_t cfg_getch(char *ch, void *param)
-{
- return apr_file_getc(ch, param);
-}
-
-static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
-{
- return apr_file_gets(buf, bufsiz, param);
-}
-
-/* Read one line from open ap_configfile_t, strip LF, increase line number */
-/* If custom handler does not define a getstr() function, read char by char */
-static apr_status_t cfg_getline_core(char *buf, apr_size_t bufsize,
- apr_size_t offset, ap_configfile_t *cfp)
-{
- apr_status_t rc;
- /* If a "get string" function is defined, use it */
- if (cfp->getstr != NULL) {
- char *cp;
- char *cbuf = buf + offset;
- apr_size_t cbufsize = bufsize - offset;
-
- while (1) {
- ++cfp->line_number;
- rc = cfp->getstr(cbuf, cbufsize, cfp->param);
- if (rc == APR_EOF) {
- if (cbuf != buf + offset) {
- *cbuf = '\0';
- break;
- }
- else {
- return APR_EOF;
- }
- }
- if (rc != APR_SUCCESS) {
- return rc;
- }
-
- /*
- * check for line continuation,
- * i.e. match [^\\]\\[\r]\n only
- */
- cp = cbuf;
- cp += strlen(cp);
- if (cp > buf && cp[-1] == LF) {
- cp--;
- if (cp > buf && cp[-1] == CR)
- cp--;
- if (cp > buf && cp[-1] == '\\') {
- cp--;
- /*
- * line continuation requested -
- * then remove backslash and continue
- */
- cbufsize -= (cp-cbuf);
- cbuf = cp;
- continue;
- }
- }
- else if (cp - buf >= bufsize - 1) {
- return APR_ENOSPC;
- }
- break;
- }
- } else {
- /* No "get string" function defined; read character by character */
- apr_size_t i = offset;
-
- if (bufsize < 2) {
- /* too small, assume caller is crazy */
- return APR_EINVAL;
- }
- buf[offset] = '\0';
-
- while (1) {
- char c;
- rc = cfp->getch(&c, cfp->param);
- if (rc == APR_EOF) {
- if (i > offset)
- break;
- else
- return APR_EOF;
- }
- if (rc != APR_SUCCESS)
- return rc;
- if (c == LF) {
- ++cfp->line_number;
- /* check for line continuation */
- if (i > 0 && buf[i-1] == '\\') {
- i--;
- continue;
- }
- else {
- break;
- }
- }
- buf[i] = c;
- ++i;
- if (i >= bufsize - 1) {
- return APR_ENOSPC;
- }
- }
- buf[i] = '\0';
- }
- return APR_SUCCESS;
-}
-
-static int cfg_trim_line(char *buf)
-{
- char *start, *end;
- /*
- * Leading and trailing white space is eliminated completely
- */
- start = buf;
- while (apr_isspace(*start))
- ++start;
- /* blast trailing whitespace */
- end = &start[strlen(start)];
- while (--end >= start && apr_isspace(*end))
- *end = '\0';
- /* Zap leading whitespace by shifting */
- if (start != buf)
- memmove(buf, start, end - start + 2);
-#ifdef DEBUG_CFG_LINES
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
-#endif
- return end - start + 1;
-}
-
-/* Read one line from open ap_configfile_t, strip LF, increase line number */
-/* If custom handler does not define a getstr() function, read char by char */
-static apr_status_t cfg_getline(char *buf, apr_size_t bufsize,
- ap_configfile_t *cfp)
-{
- apr_status_t rc = cfg_getline_core(buf, bufsize, 0, cfp);
- if (rc == APR_SUCCESS)
- cfg_trim_line(buf);
- return rc;
-}
-
-static char *substring_conf(apr_pool_t *p, const char *start, int len,
- char quote)
-{
- char *result = apr_palloc(p, len + 1);
- char *resp = result;
- int i;
-
- for (i = 0; i < len; ++i) {
- if (start[i] == '\\' && (start[i + 1] == '\\'
- || (quote && start[i + 1] == quote)))
- *resp++ = start[++i];
- else
- *resp++ = start[i];
- }
-
- *resp++ = '\0';
-#if RESOLVE_ENV_PER_TOKEN
- return (char *)ap_resolve_env(p,result);
-#else
- return result;
-#endif
-}
-
-static char *getword_conf(apr_pool_t *p, const char **line)
-{
- const char *str = *line, *strend;
- char *res;
- char quote;
-
- while (apr_isspace(*str))
- ++str;
-
- if (!*str) {
- *line = str;
- return "";
- }
-
- if ((quote = *str) == '"' || quote == '\'') {
- strend = str + 1;
- while (*strend && *strend != quote) {
- if (*strend == '\\' && strend[1] &&
- (strend[1] == quote || strend[1] == '\\')) {
- strend += 2;
- }
- else {
- ++strend;
- }
- }
- res = substring_conf(p, str + 1, strend - str - 1, quote);
-
- if (*strend == quote)
- ++strend;
- }
- else {
- strend = str;
- while (*strend && !apr_isspace(*strend))
- ++strend;
-
- res = substring_conf(p, str, strend - str, 0);
- }
-
- while (apr_isspace(*strend))
- ++strend;
- *line = strend;
- return res;
-}
-
-/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
-static apr_status_t pcfg_openfile(ap_configfile_t **ret_cfg,
- apr_pool_t *p, const char *name)
-{
- ap_configfile_t *new_cfg;
- apr_file_t *file = NULL;
- apr_finfo_t finfo;
- apr_status_t status;
-#ifdef DEBUG
- char buf[120];
-#endif
-
- if (name == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
- "Internal error: pcfg_openfile() called with NULL filename");
- return APR_EBADF;
- }
-
- status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
- APR_OS_DEFAULT, p);
-#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
- "Opening config file %s (%s)",
- name, (status != APR_SUCCESS) ?
- apr_strerror(status, buf, sizeof(buf)) : "successful");
-#endif
- if (status != APR_SUCCESS)
- return status;
-
- status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
- if (status != APR_SUCCESS)
- return status;
-
- if (finfo.filetype != APR_REG &&
- strcmp(name, "/dev/null") != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
- "Access to file %s denied by server: not a regular file",
- name);
- apr_file_close(file);
- return APR_EBADF;
- }
-
- new_cfg = apr_palloc(p, sizeof(*new_cfg));
- new_cfg->param = file;
- new_cfg->name = apr_pstrdup(p, name);
- new_cfg->getch = cfg_getch;
- new_cfg->getstr = cfg_getstr;
- new_cfg->close = cfg_close;
- new_cfg->line_number = 0;
- *ret_cfg = new_cfg;
- return APR_SUCCESS;
-}
-
-static const command_rec *find_command(const char *name,
- const command_rec *cmds)
-{
- while (cmds->name) {
- if (strcasecmp(name, cmds->name) == 0)
- return cmds;
- ++cmds;
- }
-
- return NULL;
-}
-
-static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
- void *mconfig, const char *args)
-{
- int override_list_ok = 0;
- char *w, *w2, *w3;
- const char *errmsg = NULL;
-
- /** Have we been provided a list of acceptable directives? */
- if (parms->override_list != NULL) {
- if (apr_table_get(parms->override_list, cmd->name) != NULL) {
- override_list_ok = 1;
- }
- }
-
- if ((parms->override & cmd->req_override) == 0 && !override_list_ok) {
- return apr_pstrcat(parms->pool, cmd->name,
- " not allowed here", NULL);
- }
-
- parms->info = cmd->cmd_data;
- parms->cmd = cmd;
-
- switch (cmd->args_how) {
- case RAW_ARGS:
-#ifdef RESOLVE_ENV_PER_TOKEN
- args = ap_resolve_env(parms->pool,args);
-#endif
- return cmd->AP_RAW_ARGS(parms, mconfig, args);
-
- case TAKE_ARGV:
- {
- char *argv[MAX_ARGC];
- int argc = 0;
-
- do {
- w = getword_conf(parms->pool, &args);
- if (*w == '\0' && *args == '\0') {
- break;
- }
- argv[argc] = w;
- argc++;
- } while (argc < MAX_ARGC && *args != '\0');
-
- return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
- }
-
- case NO_ARGS:
- if (*args != 0)
- return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
- NULL);
-
- return cmd->AP_NO_ARGS(parms, mconfig);
-
- case TAKE1:
- w = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE1(parms, mconfig, w);
-
- case TAKE2:
- w = getword_conf(parms->pool, &args);
- w2 = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *w2 == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE2(parms, mconfig, w, w2);
-
- case TAKE12:
- w = getword_conf(parms->pool, &args);
- w2 = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
-
- case TAKE3:
- w = getword_conf(parms->pool, &args);
- w2 = getword_conf(parms->pool, &args);
- w3 = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name, " takes three arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
-
- case TAKE23:
- w = getword_conf(parms->pool, &args);
- w2 = getword_conf(parms->pool, &args);
- w3 = *args ? getword_conf(parms->pool, &args) : NULL;
-
- if (*w == '\0' || *w2 == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name,
- " takes two or three arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
-
- case TAKE123:
- w = getword_conf(parms->pool, &args);
- w2 = *args ? getword_conf(parms->pool, &args) : NULL;
- w3 = *args ? getword_conf(parms->pool, &args) : NULL;
-
- if (*w == '\0' || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name,
- " takes one, two or three arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
-
- case TAKE13:
- w = getword_conf(parms->pool, &args);
- w2 = *args ? getword_conf(parms->pool, &args) : NULL;
- w3 = *args ? getword_conf(parms->pool, &args) : NULL;
-
- if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
- return apr_pstrcat(parms->pool, cmd->name,
- " takes one or three arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
-
- case ITERATE:
- w = getword_conf(parms->pool, &args);
-
- if (*w == '\0')
- return apr_pstrcat(parms->pool, cmd->name,
- " requires at least one argument",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- while (*w != '\0') {
- errmsg = cmd->AP_TAKE1(parms, mconfig, w);
-
- if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
- return errmsg;
-
- w = getword_conf(parms->pool, &args);
- }
-
- return errmsg;
-
- case ITERATE2:
- w = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *args == 0)
- return apr_pstrcat(parms->pool, cmd->name,
- " requires at least two arguments",
- cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
-
- while (*(w2 = getword_conf(parms->pool, &args)) != '\0') {
-
- errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
-
- if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
- return errmsg;
- }
-
- return errmsg;
-
- case FLAG:
- /*
- * This is safe to use temp_pool here, because the 'flag' itself is not
- * forwarded as-is
- */
- w = getword_conf(parms->temp_pool, &args);
-
- if (*w == '\0' || (strcasecmp(w, "on") != 0 && strcasecmp(w, "off") != 0))
- return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
- NULL);
-
- return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
-
- default:
- return apr_pstrcat(parms->pool, cmd->name,
- " is improperly configured internally (server bug)",
- NULL);
- }
-}
-
-static int is_directory(apr_pool_t *p, const char *path)
-{
- apr_finfo_t finfo;
-
- if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
- return 0; /* in error condition, just return no */
-
- return (finfo.filetype == APR_DIR);
-}
-
-static char *make_full_path(apr_pool_t *a, const char *src1,
- const char *src2)
-{
- apr_size_t len1, len2;
- char *path;
-
- len1 = strlen(src1);
- len2 = strlen(src2);
- /* allocate +3 for '/' delimiter, trailing NULL and overallocate
- * one extra byte to allow the caller to add a trailing '/'
- */
- path = (char *)apr_palloc(a, len1 + len2 + 3);
- if (len1 == 0) {
- *path = '/';
- memcpy(path + 1, src2, len2 + 1);
- }
- else {
- char *next;
- memcpy(path, src1, len1);
- next = path + len1;
- if (next[-1] != '/') {
- *next++ = '/';
- }
- memcpy(next, src2, len2 + 1);
- }
- return path;
-}
-
-static int fname_alphasort(const void *fn1, const void *fn2)
-{
- const fnames *f1 = fn1;
- const fnames *f2 = fn2;
-
- return strcmp(f1->fname,f2->fname);
-}
-
-static const char *process_resource_config(const char *fname,
- apr_array_header_t *ari,
- apr_pool_t *p,
- apr_pool_t *ptemp)
-{
- *(char **)apr_array_push(ari) = (char *)fname;
- return NULL;
-}
-
-static const char *process_resource_config_nofnmatch(const char *fname,
- apr_array_header_t *ari,
- apr_pool_t *p,
- apr_pool_t *ptemp,
- unsigned depth,
- int optional)
-{
- const char *error;
- apr_status_t rv;
-
- if (is_directory(ptemp, fname)) {
- apr_dir_t *dirp;
- apr_finfo_t dirent;
- int current;
- apr_array_header_t *candidates = NULL;
- fnames *fnew;
- char *path = apr_pstrdup(ptemp, fname);
-
- if (++depth > MAX_INCLUDE_DIR_DEPTH) {
- return apr_psprintf(p, "Directory %s exceeds the maximum include "
- "directory nesting level of %u. You have "
- "probably a recursion somewhere.", path,
- MAX_INCLUDE_DIR_DEPTH);
- }
-
- /*
- * first course of business is to grok all the directory
- * entries here and store 'em away. Recall we need full pathnames
- * for this.
- */
- rv = apr_dir_open(&dirp, path, ptemp);
- if (rv != APR_SUCCESS) {
- return apr_psprintf(p, "Could not open config directory %s: %pm",
- path, &rv);
- }
-
- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
- while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
- /* strip out '.' and '..' */
- if (strcmp(dirent.name, ".") != 0
- && strcmp(dirent.name, "..") != 0) {
- fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = make_full_path(ptemp, path, dirent.name);
- }
- }
-
- apr_dir_close(dirp);
- if (candidates->nelts != 0) {
- qsort((void *) candidates->elts, candidates->nelts,
- sizeof(fnames), fname_alphasort);
-
- /*
- * Now recurse these... we handle errors and subdirectories
- * via the recursion, which is nice
- */
- for (current = 0; current < candidates->nelts; ++current) {
- fnew = &((fnames *) candidates->elts)[current];
- error = process_resource_config_nofnmatch(fnew->fname,
- ari, p, ptemp,
- depth, optional);
- if (error) {
- return error;
- }
- }
- }
-
- return NULL;
- }
-
- return process_resource_config(fname, ari, p, ptemp);
-}
-
-static const char *process_resource_config_fnmatch(const char *path,
- const char *fname,
- apr_array_header_t *ari,
- apr_pool_t *p,
- apr_pool_t *ptemp,
- unsigned depth,
- int optional)
-{
- const char *rest;
- apr_status_t rv;
- apr_dir_t *dirp;
- apr_finfo_t dirent;
- apr_array_header_t *candidates = NULL;
- fnames *fnew;
- int current;
-
- /* find the first part of the filename */
- rest = ap_strchr_c(fname, '/');
- if (rest) {
- fname = apr_pstrndup(ptemp, fname, rest - fname);
- rest++;
- }
-
- /* optimisation - if the filename isn't a wildcard, process it directly */
- if (!apr_fnmatch_test(fname)) {
- path = make_full_path(ptemp, path, fname);
- if (!rest) {
- return process_resource_config_nofnmatch(path,
- ari, p,
- ptemp, 0, optional);
- }
- else {
- return process_resource_config_fnmatch(path, rest,
- ari, p,
- ptemp, 0, optional);
- }
- }
-
- /*
- * first course of business is to grok all the directory
- * entries here and store 'em away. Recall we need full pathnames
- * for this.
- */
- rv = apr_dir_open(&dirp, path, ptemp);
- if (rv != APR_SUCCESS) {
- return apr_psprintf(p, "Could not open config directory %s: %pm",
- path, &rv);
- }
-
- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
- while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
- /* strip out '.' and '..' */
- if (strcmp(dirent.name, ".") != 0
- && strcmp(dirent.name, "..") != 0
- && (apr_fnmatch(fname, dirent.name,
- APR_FNM_PERIOD) == APR_SUCCESS)) {
- const char *full_path = make_full_path(ptemp, path, dirent.name);
- /* If matching internal to path, and we happen to match something
- * other than a directory, skip it
- */
- if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) {
- continue;
- }
- fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = full_path;
- }
- }
-
- apr_dir_close(dirp);
- if (candidates->nelts != 0) {
- const char *error;
-
- qsort((void *) candidates->elts, candidates->nelts,
- sizeof(fnames), fname_alphasort);
-
- /*
- * Now recurse these... we handle errors and subdirectories
- * via the recursion, which is nice
- */
- for (current = 0; current < candidates->nelts; ++current) {
- fnew = &((fnames *) candidates->elts)[current];
- if (!rest) {
- error = process_resource_config_nofnmatch(fnew->fname,
- ari, p,
- ptemp, 0, optional);
- }
- else {
- error = process_resource_config_fnmatch(fnew->fname, rest,
- ari, p,
- ptemp, 0, optional);
- }
- if (error) {
- return error;
- }
- }
- }
- else {
-
- if (!optional) {
- return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing "
- "(use IncludeOptional if required)", fname, path);
- }
- }
-
- return NULL;
-}
-
-static const char *process_fnmatch_configs(const char *fname,
- apr_array_header_t *ari,
- apr_pool_t *p,
- apr_pool_t *ptemp,
- int optional)
-{
- if (!apr_fnmatch_test(fname)) {
- return process_resource_config_nofnmatch(fname, ari, p, ptemp, 0, optional);
- }
- else {
- apr_status_t status;
- const char *rootpath, *filepath = fname;
-
- /* locate the start of the directories proper */
- status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp);
-
- /* we allow APR_SUCCESS and APR_EINCOMPLETE */
- if (APR_ERELATIVE == status) {
- return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL);
- }
- else if (APR_EBADPATH == status) {
- return apr_pstrcat(p, "Include has a bad path, ", fname, NULL);
- }
-
- /* walk the filepath */
- return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp,
- 0, optional);
- }
-}
-
-const char *read_module_config(server_rec *s, void *mconfig,
- const command_rec *cmds,
- apr_pool_t *p, apr_pool_t *ptemp,
- const char *filename)
-{
- apr_array_header_t *ari, *arr;
- ap_directive_t *newdir;
- cmd_parms *parms;
-
- char line[MAX_STRING_LEN];
- const char *errmsg;
- const char *err = NULL;
-
- ari = apr_array_make(p, 1, sizeof(char *));
- arr = apr_array_make(p, 1, sizeof(cmd_parms));
-
- errmsg = process_fnmatch_configs(filename, ari, p, ptemp, 0);
-
- if (errmsg != NULL)
- goto out;
-
- while (ari->nelts || arr->nelts) {
-
- /* similar to process_command_config() */
- if (ari->nelts) {
- char *inc = *(char **)apr_array_pop(ari);
-
- parms = (cmd_parms *)apr_array_push(arr);
- *parms = default_parms;
- parms->pool = p;
- parms->temp_pool = ptemp;
- parms->server = s;
- parms->override = (RSRC_CONF | ACCESS_CONF);
- parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI;
-
- if (pcfg_openfile(&parms->config_file, p, inc) != APR_SUCCESS) {
- apr_array_pop(arr);
- errmsg = apr_pstrcat(p, "Cannot open file: ", inc, NULL);
- goto out;
- }
- }
-
- if (arr->nelts > MAX_INCLUDE_DIR_DEPTH) {
- errmsg = apr_psprintf(p, "Exceeded the maximum include "
- "directory nesting level of %u. You have "
- "probably a recursion somewhere.",
- MAX_INCLUDE_DIR_DEPTH);
- goto out;
- }
-
- if (!(parms = (cmd_parms *)apr_array_pop(arr)))
- break;
-
- while (!(cfg_getline(line, MAX_STRING_LEN, parms->config_file))) {
-
- const command_rec *cmd;
- char *cmd_name;
- const char *args = line;
- int optional = 0;
-
- if (*line == '#' || *line == '\0')
- continue;
-
- if (!(cmd_name = getword_conf(p, &args)))
- continue;
-
- /* similar to invoke_cmd() */
- if (strcasecmp(cmd_name, "IncludeOptional") == 0 ||
- strcasecmp(cmd_name, "Include") == 0)
- {
- char *w, *fullname;
-
- if (strcasecmp(cmd_name, "IncludeOptional") == 0)
- optional = 1;
-
- w = getword_conf(parms->pool, &args);
-
- if (*w == '\0' || *args != 0) {
- errmsg = apr_pstrcat(parms->pool, cmd_name, " takes one argument", NULL);
- goto out;
- }
-
- fullname = ap_server_root_relative(ptemp, w);
- errmsg = process_fnmatch_configs(fullname, ari, p, ptemp, optional);
-
- *(cmd_parms *)apr_array_push(arr) = *parms;
-
- if(errmsg != NULL)
- goto out;
-
- parms = NULL;
- break;
- }
-
- if (!(cmd = find_command(cmd_name, cmds))) {
- errmsg = apr_pstrcat(parms->pool, "Invalid command '",
- cmd_name, "'", NULL);
- goto out;
- }
-
- newdir = apr_pcalloc(p, sizeof(ap_directive_t));
- newdir->filename = parms->config_file->name;
- newdir->line_num = parms->config_file->line_number;
- newdir->directive = cmd_name;
- newdir->args = apr_pstrdup(p, args);
-
- parms->directive = newdir;
-
- if ((errmsg = invoke_cmd(cmd, parms, mconfig, args)) != NULL)
- break;
- }
-
- if (parms != NULL)
- cfg_closefile(parms->config_file);
-
- if (errmsg != NULL)
- break;
- }
-
- if (errmsg) {
- if (parms) {
- err = apr_psprintf(p, "Syntax error on line %d of %s: %s",
- parms->config_file->line_number,
- parms->config_file->name,
- errmsg);
- errmsg = err;
- }
- }
-
-out:
-
- while ((parms = (cmd_parms *)apr_array_pop(arr)) != NULL)
- cfg_closefile(parms->config_file);
-
- return errmsg;
-}
-
-int lookup_builtin_method(const char *method, apr_size_t len)
-{
- /* Note: from Apache 2 HTTP Server source. */
-
- /* Note: the following code was generated by the "shilka" tool from
- the "cocom" parsing/compilation toolkit. It is an optimized lookup
- based on analysis of the input keywords. Postprocessing was done
- on the shilka output, but the basic structure and analysis is
- from there. Should new HTTP methods be added, then manual insertion
- into this code is fine, or simply re-running the shilka tool on
- the appropriate input. */
-
- /* Note: it is also quite reasonable to just use our method_registry,
- but I'm assuming (probably incorrectly) we want more speed here
- (based on the optimizations the previous code was doing). */
-
- switch (len)
- {
- case 3:
- switch (method[0])
- {
- case 'P':
- return (method[1] == 'U'
- && method[2] == 'T'
- ? M_PUT : UNKNOWN_METHOD);
- case 'G':
- return (method[1] == 'E'
- && method[2] == 'T'
- ? M_GET : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 4:
- switch (method[0])
- {
- case 'H':
- return (method[1] == 'E'
- && method[2] == 'A'
- && method[3] == 'D'
- ? M_GET : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'O'
- && method[2] == 'S'
- && method[3] == 'T'
- ? M_POST : UNKNOWN_METHOD);
- case 'M':
- return (method[1] == 'O'
- && method[2] == 'V'
- && method[3] == 'E'
- ? M_MOVE : UNKNOWN_METHOD);
- case 'L':
- return (method[1] == 'O'
- && method[2] == 'C'
- && method[3] == 'K'
- ? M_LOCK : UNKNOWN_METHOD);
- case 'C':
- return (method[1] == 'O'
- && method[2] == 'P'
- && method[3] == 'Y'
- ? M_COPY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 5:
- switch (method[2])
- {
- case 'T':
- return (memcmp(method, "PATCH", 5) == 0
- ? M_PATCH : UNKNOWN_METHOD);
- case 'R':
- return (memcmp(method, "MERGE", 5) == 0
- ? M_MERGE : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "MKCOL", 5) == 0
- ? M_MKCOL : UNKNOWN_METHOD);
- case 'B':
- return (memcmp(method, "LABEL", 5) == 0
- ? M_LABEL : UNKNOWN_METHOD);
- case 'A':
- return (memcmp(method, "TRACE", 5) == 0
- ? M_TRACE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 6:
- switch (method[0])
- {
- case 'U':
- switch (method[5])
- {
- case 'K':
- return (memcmp(method, "UNLOCK", 6) == 0
- ? M_UNLOCK : UNKNOWN_METHOD);
- case 'E':
- return (memcmp(method, "UPDATE", 6) == 0
- ? M_UPDATE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
- case 'R':
- return (memcmp(method, "REPORT", 6) == 0
- ? M_REPORT : UNKNOWN_METHOD);
- case 'D':
- return (memcmp(method, "DELETE", 6) == 0
- ? M_DELETE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 7:
- switch (method[1])
- {
- case 'P':
- return (memcmp(method, "OPTIONS", 7) == 0
- ? M_OPTIONS : UNKNOWN_METHOD);
- case 'O':
- return (memcmp(method, "CONNECT", 7) == 0
- ? M_CONNECT : UNKNOWN_METHOD);
- case 'H':
- return (memcmp(method, "CHECKIN", 7) == 0
- ? M_CHECKIN : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 8:
- switch (method[0])
- {
- case 'P':
- return (memcmp(method, "PROPFIND", 8) == 0
- ? M_PROPFIND : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "CHECKOUT", 8) == 0
- ? M_CHECKOUT : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 9:
- return (memcmp(method, "PROPPATCH", 9) == 0
- ? M_PROPPATCH : UNKNOWN_METHOD);
-
- case 10:
- switch (method[0])
- {
- case 'U':
- return (memcmp(method, "UNCHECKOUT", 10) == 0
- ? M_UNCHECKOUT : UNKNOWN_METHOD);
- case 'M':
- return (memcmp(method, "MKACTIVITY", 10) == 0
- ? M_MKACTIVITY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 11:
- return (memcmp(method, "MKWORKSPACE", 11) == 0
- ? M_MKWORKSPACE : UNKNOWN_METHOD);
-
- case 15:
- return (memcmp(method, "VERSION-CONTROL", 15) == 0
- ? M_VERSION_CONTROL : UNKNOWN_METHOD);
-
- case 16:
- return (memcmp(method, "BASELINE-CONTROL", 16) == 0
- ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
-
- default:
- return UNKNOWN_METHOD;
- }
-
- /* NOTREACHED */
-}
+++ /dev/null
-/*
- * Mod Defender for HAProxy
- *
- * Support for the Mod Defender code on non-Apache platforms.
- *
- * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
- *
- * Parts of code based on Apache HTTP Server source
- * Copyright 2015 The Apache Software Foundation (http://www.apache.org/)
- *
- * 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
- * 3 of the License, or (at your option) any later version.
- *
- */
-#ifndef __STANDALONE_H__
-#define __STANDALONE_H__
-
-#include <http_core.h>
-#include <http_main.h>
-#include <http_config.h>
-
-#include <apr_pools.h>
-#include <apr_hooks.h>
-
-#define INSERT_BEFORE(f, before_this) ((before_this) == NULL \
- || (before_this)->frec->ftype > (f)->frec->ftype \
- || (before_this)->r != (f)->r)
-
-#define DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \
-ns##_HOOK_##name##_t *run_##ns##_hook_##name = NULL; \
-link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf, \
- const char * const *aszPre, \
- const char * const *aszSucc, int nOrder) \
-{ \
- run_##ns##_hook_##name = pf; \
-}
-
-#define DECLARE_HOOK(ret,name,args) \
- DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args)
-
-#define UNKNOWN_METHOD (-1)
-
-extern void (*logger)(int level, char *str);
-extern const char *read_module_config(server_rec *s, void *mconfig,
- const command_rec *cmds,
- apr_pool_t *p, apr_pool_t *ptemp,
- const char *filename);
-extern int lookup_builtin_method(const char *method, apr_size_t len);
-
-#endif /* __STANDALONE_H__ */