[-]
[+]
|
Changed |
_service
|
@@ -3,5 +3,5 @@
- <param name="protocol">http</param><param name="host">haproxy.1wt.eu</param><param name="path">/download/1.5/src/devel/haproxy-1.5-dev23.tar.gz</param></service>
+ <param name="protocol">http</param><param name="host">haproxy.1wt.eu</param><param name="path">/download/1.5/src/devel/haproxy-1.5-dev25.tar.gz</param></service>
</services>
\ No newline at end of file
|
[-]
[+]
|
Deleted |
_service:download_url:haproxy-1.5-dev23.tar.gz/Makefile.bsd
^
|
@@ -1,153 +0,0 @@
-# This makefile is dedicated to OpenBSD (and possibly other BSDs)
-# You should use it this way :
-# make TARGET=os CPU=cpu
-#
-# Some optional components may be added, such as DLMALLOC :
-#
-# make TARGET=freebsd CPU=i686 DLMALLOC_SRC=/usr/local/src/dlmalloc.c \
-# OPT_OBJS=src/dlmalloc.o
-
-# Select target OS. TARGET must match a system for which COPTS and LIBS are
-# correctly defined below.
-TARGET = openbsd
-
-# pass CPU=<cpu_name> to make to optimize for a particular CPU
-CPU = generic
-#CPU = native
-#CPU = i586
-#CPU = i686
-#CPU = ultrasparc
-
-# By default, we use libc's regex. WARNING! On Solaris 8/Sparc, group
-# references seem broken using libc ! Use pcre instead.
-REGEX=libc
-#REGEX=pcre
-#REGEX=static-pcre
-
-# tools options
-CC = gcc
-LD = gcc
-
-# This is the directory hosting include/pcre.h and lib/libpcre.* when REGEX=pcre
-PCREDIR!= pcre-config --prefix 2>/dev/null || :
-#PCREDIR=/usr/local
-
-# This is for OpenBSD 3.0 and above
-COPTS.openbsd = -DENABLE_POLL -DENABLE_KQUEUE
-LIBS.openbsd =
-
-# CPU dependant optimizations
-COPTS.generic = -O2
-COPTS.native = -O2 -march=native
-COPTS.i586 = -O2 -march=i586
-COPTS.i686 = -O2 -march=i686
-COPTS.ultrasparc = -O6 -mcpu=v9 -mtune=ultrasparc
-
-# options for standard regex library
-COPTS.libc=
-LIBS.libc=
-
-# options for libpcre
-COPTS.pcre=-DUSE_PCRE -I$(PCREDIR)/include
-LIBS.pcre=-L$(PCREDIR)/lib -lpcreposix -lpcre
-
-# options for static libpcre
-COPTS.static-pcre=-DUSE_PCRE -I$(PCREDIR)/include
-LIBS.static-pcre=-L$(PCREDIR)/lib -Wl,-Bstatic -lpcreposix -lpcre -Wl,-Bdynamic
-
-# you can enable debug arguments with "DEBUG=-g" or disable them with "DEBUG="
-#DEBUG = -g -DDEBUG_MEMORY -DDEBUG_FULL
-DEBUG = -g
-
-# if small memory footprint is required, you can reduce the buffer size. There
-# are 2 buffers per concurrent session, so 16 kB buffers will eat 32 MB memory
-# with 1000 concurrent sessions. Putting it slightly lower than a page size
-# will avoid the additionnal paramters to overflow a page. 8030 bytes is
-# exactly 5.5 TCP segments of 1460 bytes.
-#SMALL_OPTS =
-SMALL_OPTS = -DBUFSIZE=8030 -DMAXREWRITE=1030 -DSYSTEM_MAXCONN=1024
-
-# redefine this if you want to add some special PATH to include/libs
-ADDINC =
-ADDLIB =
-
-# redefine this if you want to add some special .o files
-OPT_OBJS =
-
-# set some defines when needed.
-# Known ones are -DENABLE_POLL
-# - use -DTPROXY to compile with transparent proxy support.
-# - use -DUSE_GETADDRINFO to use of getaddrinfo() to resolve IPv6 host names
-DEFINE = -DTPROXY
-
-# May be changed to patch PAGE_SIZE on every platform when using dlmalloc
-DLMALLOC_THRES=4096
-
-# global options
-TARGET_OPTS=$(COPTS.$(TARGET))
-REGEX_OPTS=$(COPTS.$(REGEX))
-CPU_OPTS=$(COPTS.$(CPU))
-SPEC_OPTS=-fno-strict-aliasing
-
-VERSION != cat VERSION 2>/dev/null || touch VERSION
-SUBVERS != cat SUBVERS 2>/dev/null || touch SUBVERS
-VERDATE != cat VERDATE 2>/dev/null || touch VERDATE
-
-VER_OPTS := -DCONFIG_HAPROXY_VERSION=\"$(VERSION)$(SUBVERS)\" \
- -DCONFIG_HAPROXY_DATE=\"$(VERDATE)\"
-
-# This one can be changed to look for ebtree files in an external directory
-EBTREE_DIR := ebtree
-
-COPTS = -Iinclude -I$(EBTREE_DIR) $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) \
- $(SPEC_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(VER_OPTS) $(DEFINE)
-LIBS = $(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
-CFLAGS = -Wall $(COPTS) $(DEBUG)
-LDFLAGS = -g
-
-OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
- src/uri_auth.o src/standard.o src/buffer.o src/log.o src/task.o \
- src/chunk.o src/channel.o src/listener.o \
- src/time.o src/fd.o src/pipe.o src/regex.o src/cfgparse.o src/server.o \
- src/checks.o src/queue.o src/frontend.o src/proxy.o src/proto_uxst.o \
- src/proto_http.o src/raw_sock.o src/appsession.o src/backend.o \
- src/peers.o src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
- src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
- src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
- src/ev_poll.o src/ev_kqueue.o src/connection.o \
- src/arg.o src/acl.o src/memory.o src/freq_ctr.o src/payload.o \
- src/auth.o src/stick_table.o src/sample.o src/compression.o \
- src/hash.o src/pattern.o src/map.o
-
-EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
- $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
- $(EBTREE_DIR)/ebmbtree.o $(EBTREE_DIR)/ebsttree.o \
- $(EBTREE_DIR)/ebimtree.o $(EBTREE_DIR)/ebistree.o
-
-all: haproxy
-
-haproxy: $(OBJS) $(OPT_OBJS) $(EBTREE_OBJS)
- $(LD) $(LDFLAGS) -o $@ $> $(LIBS)
-
-.SUFFIXES: .c.o
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $>
-
-src/haproxy.o: src/haproxy.c
- $(CC) $(CFLAGS) -DBUILD_TARGET='"$(TARGET)"' -DBUILD_CC='"$(CC)"' \
- -DBUILD_CPU='"$(CPU)"' -DBUILD_REGEX='"$(REGEX)"' \
- -DBUILD_OPTS='"$(COPTS)"' -c -o $@ $>
-
-src/dlmalloc.o: $(DLMALLOC_SRC)
- $(CC) $(CFLAGS) -DDEFAULT_MMAP_THRESHOLD=$(DLMALLOC_THRES) -c -o $@ $>
-
-clean:
- rm -f *.[oas] src/*.[oas] ebtree/*.[oas] haproxy test
- for dir in . src include/* doc ebtree; do rm -f $$dir/*~ $$dir/*.rej $$dir/core; done
- rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION) nohup.out gmon.out
-
-version:
- @echo "VERSION: $(VERSION)"
- @echo "SUBVERS: $(SUBVERS)"
- @echo "VERDATE: $(VERDATE)"
|
[-]
[+]
|
Deleted |
_service:download_url:haproxy-1.5-dev23.tar.gz/Makefile.osx
^
|
@@ -1,150 +0,0 @@
-# This makefile is dedicated to darwin (and possibly other BSDs)
-# You should use it this way :
-# make TARGET=os CPU=cpu
-#
-# Some optional components may be added, such as DLMALLOC :
-#
-# make DLMALLOC_SRC=/usr/local/src/dlmalloc.c \
-# OPT_OBJS=src/dlmalloc.o
-
-# Select target OS. TARGET must match a system for which COPTS and LIBS are
-# correctly defined below.
-TARGET = generic
-
-# pass CPU=<cpu_name> to make to optimize for a particular CPU
-CPU = generic
-#CPU = native
-#CPU = i586
-#CPU = i686
-#CPU = ultrasparc
-
-# By default, we use libc's regex. WARNING! On Solaris 8/Sparc, group
-# references seem broken using libc ! Use pcre instead.
-REGEX=libc
-#REGEX=pcre
-#REGEX=static-pcre
-
-# tools options
-CC = gcc
-LD = gcc
-
-# This is the directory hosting include/pcre.h and lib/libpcre.* when REGEX=pcre
-PCREDIR!= pcre-config --prefix 2>/dev/null || :
-#PCREDIR=/usr/local
-
-# This one can be changed to look for ebtree files in an external directory
-EBTREE_DIR = ebtree
-
-# This is for darwin 3.0 and above
-COPTS.darwin = -DENABLE_POLL -DENABLE_KQUEUE
-LIBS.darwin =
-
-# CPU dependant optimizations
-COPTS.generic = -O2
-COPTS.native = -O2 -march=native
-COPTS.i586 = -O2 -march=i586
-COPTS.i686 = -O2 -march=i686
-COPTS.ultrasparc = -O6 -mcpu=v9 -mtune=ultrasparc
-
-# options for standard regex library
-COPTS.libc=
-LIBS.libc=
-
-# options for libpcre
-COPTS.pcre=-DUSE_PCRE -I$(PCREDIR)/include
-LIBS.pcre=-L$(PCREDIR)/lib -lpcreposix -lpcre
-
-# options for static libpcre
-COPTS.static-pcre=-DUSE_PCRE -I$(PCREDIR)/include
-LIBS.static-pcre=-L$(PCREDIR)/lib -Wl,-Bstatic -lpcreposix -lpcre -Wl,-Bdynamic
-
-# you can enable debug arguments with "DEBUG=-g" or disable them with "DEBUG="
-#DEBUG = -g -DDEBUG_MEMORY -DDEBUG_FULL
-DEBUG = -g
-
-# if small memory footprint is required, you can reduce the buffer size. There
-# are 2 buffers per concurrent session, so 16 kB buffers will eat 32 MB memory
-# with 1000 concurrent sessions. Putting it slightly lower than a page size
-# will avoid the additionnal paramters to overflow a page. 8030 bytes is
-# exactly 5.5 TCP segments of 1460 bytes.
-#SMALL_OPTS =
-SMALL_OPTS = -DBUFSIZE=8030 -DMAXREWRITE=1030 -DSYSTEM_MAXCONN=1024
-
-# redefine this if you want to add some special PATH to include/libs
-ADDINC =
-ADDLIB =
-
-# set some defines when needed.
-# Known ones are -DENABLE_POLL
-# - use -DTPROXY to compile with transparent proxy support.
-# - use -DUSE_GETADDRINFO to use of getaddrinfo() to resolve IPv6 host names
-DEFINE = -DTPROXY
-
-# May be changed to patch PAGE_SIZE on every platform when using dlmalloc
-DLMALLOC_THRES=4096
-
-# global options
-TARGET_OPTS=$(COPTS.$(TARGET))
-REGEX_OPTS=$(COPTS.$(REGEX))
-CPU_OPTS=$(COPTS.$(CPU))
-SPEC_OPTS=-fno-strict-aliasing
-
-VERSION != cat VERSION 2>/dev/null || touch VERSION
-SUBVERS != cat SUBVERS 2>/dev/null || touch SUBVERS
-VERDATE != cat VERDATE 2>/dev/null || touch VERDATE
-
-VER_OPTS := -DCONFIG_HAPROXY_VERSION=\"$(VERSION)$(SUBVERS)\" \
- -DCONFIG_HAPROXY_DATE=\"$(VERDATE)\"
-
-COPTS = -Iinclude -I$(EBTREE_DIR) $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) \
- $(SPEC_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(VER_OPTS) $(DEFINE)
-LIBS = $(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
-CFLAGS = -Wall $(COPTS) $(DEBUG) -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386 -mmacosx-version-min=10.4
-LDFLAGS = -g -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386 -mmacosx-version-min=10.4
-
-OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
- src/uri_auth.o src/standard.o src/buffer.o src/log.o src/task.o \
- src/chunk.o src/channel.o src/listener.o \
- src/time.o src/fd.o src/pipe.o src/regex.o src/cfgparse.o src/server.o \
- src/checks.o src/queue.o src/frontend.o src/proxy.o src/proto_uxst.o \
- src/proto_http.o src/raw_sock.o src/appsession.o src/backend.o \
- src/peers.o src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
- src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
- src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
- src/ev_poll.o src/connection.o src/payload.o \
- src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
- src/auth.o src/stick_table.o src/sample.o src/compression.o \
- src/hash.o src/pattern.o src/map.o
-
-EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
- $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
- $(EBTREE_DIR)/ebmbtree.o $(EBTREE_DIR)/ebsttree.o \
- $(EBTREE_DIR)/ebimtree.o $(EBTREE_DIR)/ebistree.o
-
-all: haproxy
-
-haproxy: $(OBJS) $(EBTREE_OBJS)
- $(LD) $(LDFLAGS) $(OBJS) $(EBTREE_OBJS) -o $@
-
-.SUFFIXES: .c.o
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $<
-
-src/haproxy.o: src/haproxy.c
- $(CC) $(CFLAGS) -DBUILD_TARGET='"$(TARGET)"' -DBUILD_CC='"$(CC)"' \
- -DBUILD_CPU='"$(CPU)"' -DBUILD_REGEX='"$(REGEX)"' \
- -DBUILD_OPTS='"$(COPTS)"' -c -o $@ $<
-
-src/dlmalloc.o: $(DLMALLOC_SRC)
- $(CC) $(CFLAGS) -DDEFAULT_MMAP_THRESHOLD=$(DLMALLOC_THRES) -c -o $@ $<
-
-clean:
- rm -f *.[oas] src/*.[oas] ebtree/*.[oas] haproxy test
- for dir in . src include/* doc ebtree; do rm -f $$dir/*~ $$dir/*.rej $$dir/core; done
- rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION) nohup.out gmon.out
-
-version:
- @echo "VERSION: $(VERSION)"
- @echo "SUBVERS: $(SUBVERS)"
- @echo "VERDATE: $(VERDATE)"
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/CHANGELOG
^
|
@@ -1,6 +1,114 @@
ChangeLog :
===========
+2014/05/10 : 1.5-dev25
+ - MEDIUM: connection: Implement and extented PROXY Protocol V2
+ - MINOR: ssl: clean unused ACLs declarations
+ - MINOR: ssl: adds fetchs and ACLs for ssl back connection.
+ - MINOR: ssl: merge client's and frontend's certificate functions.
+ - MINOR: ssl: adds ssl_f_sha1 fetch to return frontend's certificate fingerprint
+ - MINOR: ssl: adds sample converter base64 for binary type.
+ - MINOR: ssl: convert to binary ssl_fc_unique_id and ssl_bc_unique_id.
+ - BUG/MAJOR: ssl: Fallback to private session cache if current lock mode is not supported.
+ - MAJOR: ssl: Change default locks on ssl session cache.
+ - BUG/MINOR: chunk: Fix function chunk_strcmp and chunk_strcasecmp match a substring.
+ - MINOR: ssl: add global statement tune.ssl.force-private-cache.
+ - MINOR: ssl: remove fallback to SSL session private cache if lock init fails.
+ - BUG/MEDIUM: patterns: last fix was still not enough
+ - MINOR: http: export the smp_fetch_cookie function
+ - MINOR: http: generic pointer to rule argument
+ - BUG/MEDIUM: pattern: a typo breaks automatic acl/map numbering
+ - BUG/MAJOR: patterns: -i and -n are ignored for inlined patterns
+ - BUG/MINOR: proxy: unsafe initialization of HTTP transaction when switching from TCP frontend
+ - BUG/MINOR: http: log 407 in case of proxy auth
+ - MINOR: http: rely on the message body parser to send 100-continue
+ - MEDIUM: http: move reqadd after execution of http_request redirect
+ - MEDIUM: http: jump to dedicated labels after http-request processing
+ - BUG/MINOR: http: block rules forgot to increment the denied_req counter
+ - BUG/MINOR: http: block rules forgot to increment the session's request counter
+ - MEDIUM: http: move Connection header processing earlier
+ - MEDIUM: http: remove even more of the spaghetti in the request path
+ - MINOR: http: silently support the "block" action for http-request
+ - CLEANUP: proxy: rename "block_cond" to "block_rules"
+ - MEDIUM: http: emulate "block" rules using "http-request" rules
+ - MINOR: http: remove the now unused loop over "block" rules
+ - MEDIUM: http: factorize the "auth" action of http-request and stats
+ - MEDIUM: http: make http-request rules processing return a verdict instead of a rule
+ - MINOR: config: add minimum support for emitting warnings only once
+ - MEDIUM: config: inform the user about the deprecatedness of "block" rules
+ - MEDIUM: config: inform the user that "reqsetbe" is deprecated
+ - MEDIUM: config: inform the user only once that "redispatch" is deprecated
+ - MEDIUM: config: warn that '{cli,con,srv}timeout' are deprecated
+ - BUG/MINOR: auth: fix wrong return type in pat_match_auth()
+ - BUILD: config: remove a warning with clang
+ - BUG/MAJOR: http: connection setup may stall on balance url_param
+ - BUG/MEDIUM: http/session: disable client-side expiration only after body
+ - BUG/MEDIUM: http: correctly report request body timeouts
+ - BUG/MEDIUM: http: disable server-side expiration until client has sent the body
+ - MEDIUM: listener: make the accept function more robust against pauses
+ - BUILD: syscalls: remove improper inline statement in front of syscalls
+ - BUILD: ssl: SSL_CTX_set_msg_callback() needs openssl >= 0.9.7
+ - BUG/MAJOR: session: recover the correct connection pointer in half-initialized sessions
+ - DOC: add some explanation on the shared cache build options in the readme.
+ - MEDIUM: proxy: only adjust the backend's bind-process when already set
+ - MEDIUM: config: limit nbproc to the machine's word size
+ - MEDIUM: config: check the bind-process settings according to nbproc
+ - MEDIUM: listener: parse the new "process" bind keyword
+ - MEDIUM: listener: inherit the process mask from the proxy
+ - MAJOR: listener: only start listeners bound to the same processes
+ - MINOR: config: only report a warning when stats sockets are bound to more than 1 process
+ - CLEANUP: config: set the maxaccept value for peers listeners earlier
+ - BUG/MINOR: backend: only match IPv4 addresses with RDP cookies
+ - BUG/MINOR: checks: correctly configure the address family and protocol
+ - MINOR: tools: split is_addr() and is_inet_addr()
+ - MINOR: protocols: use is_inet_addr() when only INET addresses are desired
+ - MEDIUM: unix: add preliminary support for connecting to servers over UNIX sockets
+ - MEDIUM: checks: only complain about the missing port when the check uses TCP
+ - MEDIUM: unix: implement support for Linux abstract namespace sockets
+ - DOC: map_beg was missing from the table of map_* converters
+ - DOC: ebtree: indicate that prefix insertion/lookup may be used with strings
+ - MEDIUM: pattern: use ebtree's longest match to index/lookup string beginning
+ - BUILD: remove the obsolete BSD and OSX makefiles
+ - MEDIUM: unix: avoid a double connect probe when no data are sent
+ - DOC: stop referencing the slow git repository in the README
+ - BUILD: only build the systemd wrapper on Linux 2.6 and above
+ - DOC: update roadmap with completed tasks
+ - MEDIUM: session: implement half-closed timeouts (client-fin and server-fin)
+
+2014/04/26 : 1.5-dev24
+ - MINOR: pattern: find element in a reference
+ - MEDIUM: http: ACL and MAP updates through http-(request|response) rules
+ - MEDIUM: ssl: explicitly log failed handshakes after a heartbeat
+ - DOC: Full section dedicated to the converters
+ - MEDIUM: http: register http-request and http-response keywords
+ - BUG/MINOR: compression: correctly report incoming byte count
+ - BUG/MINOR: http: don't report server aborts as client aborts
+ - BUG/MEDIUM: channel: bi_putblk() must not wrap before the end of buffer
+ - CLEANUP: buffers: remove unused function buffer_contig_space_with_res()
+ - MEDIUM: stats: reimplement HTTP keep-alive on the stats page
+ - BUG/MAJOR: http: fix timeouts during data forwarding
+ - BUG/MEDIUM: http: 100-continue responses must process the next part immediately
+ - MEDIUM: http: move skipping of 100-continue earlier
+ - BUILD: stats: let gcc know that last_fwd cannot be used uninitialized...
+ - CLEANUP: general: get rid of all old occurrences of "session *t"
+ - CLEANUP: http: remove the useless "if (1)" inherited from version 1.4
+ - BUG/MEDIUM: stats: mismatch between behaviour and doc about front/back
+ - MEDIUM: http: enable analysers to have keep-alive on stats
+ - REORG: http: move HTTP Connection response header parsing earlier
+ - MINOR: stats: always emit HTTP/1.1 in responses
+ - MINOR: http: add capture.req.ver and capture.res.ver
+ - MINOR: checks: add a new global max-spread-checks directive
+ - BUG/MAJOR: http: fix the 'next' pointer when performing a redirect
+ - MINOR: http: implement the max-keep-alive-queue setting
+ - DOC: fix alphabetic order of tcp-check
+ - MINOR: connection: add a new error code for SSL with heartbeat
+ - MEDIUM: ssl: implement a workaround for the OpenSSL heartbleed attack
+ - BUG/MEDIUM: Revert "MEDIUM: ssl: Add standardized DH parameters >= 1024 bits"
+ - BUILD: http: remove a warning on strndup
+ - BUILD: ssl: avoid a warning about conn not used with OpenSSL < 1.0.1
+ - BUG/MINOR: ssl: really block OpenSSL's response to heartbleed attack
+ - MINOR: ssl: finally catch the heartbeats missing the padding
+
2014/04/23 : 1.5-dev23
- BUG/MINOR: reject malformed HTTP/0.9 requests
- MINOR: systemd wrapper: re-execute on SIGUSR2
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/Makefile
^
|
@@ -17,6 +17,7 @@
# USE_PCRE_JIT : enable JIT for faster regex on libpcre >= 8.32
# USE_POLL : enable poll(). Automatic.
# USE_PRIVATE_CACHE : disable shared memory cache of ssl sessions.
+# USE_PTHREAD_PSHARED : enable pthread process shared mutex on sslcache.
# USE_REGPARM : enable regparm optimization. Recommended on x86.
# USE_STATIC_PCRE : enable static libpcre. Recommended.
# USE_TPROXY : enable transparent proxy. Automatic.
@@ -51,6 +52,8 @@
# by "haproxy -vv" in CFLAGS.
# SILENT_DEFINE may be used to specify other defines which will not be
# reported by "haproxy -vv".
+# EXTRA is used to force building or not building some extra tools. By
+# default on Linux 2.6+, it contains "haproxy-systemd-wrapper".
# DESTDIR is not set by default and is used for installation only.
# It might be useful to set DESTDIR if you want to install haproxy
# in a sandbox.
@@ -156,6 +159,10 @@
DEFINE =
SILENT_DEFINE =
+#### extra programs to build (eg: haproxy-systemd-wrapper)
+# Force this to enable building extra programs or to disable them.
+# It's automatically appended depending on the targets.
+EXTRA =
#### CPU dependant optimizations
# Some CFLAGS are set by default depending on the target CPU. Those flags only
@@ -238,6 +245,7 @@
USE_TPROXY = implicit
USE_LIBCRYPT = implicit
USE_FUTEX = implicit
+ EXTRA += haproxy-systemd-wrapper
else
ifeq ($(TARGET),linux2628)
# This is for standard Linux >= 2.6.28 with netfilter, epoll, tproxy and splice
@@ -253,6 +261,7 @@
USE_FUTEX = implicit
USE_CPU_AFFINITY= implicit
ASSUME_SPLICE_WORKS= implicit
+ EXTRA += haproxy-systemd-wrapper
else
ifeq ($(TARGET),solaris)
# This is for Solaris 8
@@ -536,10 +545,13 @@
ifneq ($(USE_PRIVATE_CACHE),)
OPTIONS_CFLAGS += -DUSE_PRIVATE_CACHE
else
+ifneq ($(USE_PTHREAD_PSHARED),)
+OPTIONS_CFLAGS += -DUSE_PTHREAD_PSHARED
+OPTIONS_LDFLAGS += -lpthread
+else
ifneq ($(USE_FUTEX),)
OPTIONS_CFLAGS += -DUSE_SYSCALL_FUTEX
-else
-OPTIONS_LDFLAGS += -lpthread
+endif
endif
endif
endif
@@ -631,7 +643,7 @@
@echo
@exit 1
else
-all: haproxy haproxy-systemd-wrapper
+all: haproxy $(EXTRA)
endif
OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/README
^
|
@@ -1,9 +1,9 @@
----------------------
HAProxy how-to
----------------------
- version 1.5-dev23
+ version 1.5-dev25
willy tarreau
- 2014/04/23
+ 2014/05/10
1) How to build it
@@ -11,7 +11,8 @@
To build haproxy, you will need :
- GNU make. Neither Solaris nor OpenBSD's make work with the GNU Makefile.
- However, specific Makefiles for BSD and OSX are provided.
+ If you get many syntax errors when running "make", you may want to retry
+ with "gmake" which is the name commonly used for GNU make on BSD systems.
- GCC between 2.91 and 4.7. Others may work, but not tested.
- GNU ld
@@ -85,11 +86,11 @@
working fine on all recent mainstream distros. It is automatically enabled on
Solaris 8 and above, as it's known to work.
-It is possible to add native support for SSL using the GNU makefile only, and
-by passing "USE_OPENSSL=1" on the make commande line. The libssl and libcrypto
-will automatically be linked with haproxy. Some systems also require libz, so
-if the build fails due to missing symbols such as deflateInit(), then try again
-with "ADDLIB=-lz".
+It is possible to add native support for SSL using the GNU makefile, by passing
+"USE_OPENSSL=1" on the make command line. The libssl and libcrypto will
+automatically be linked with haproxy. Some systems also require libz, so if the
+build fails due to missing symbols such as deflateInit(), then try again with
+"ADDLIB=-lz".
To link OpenSSL statically against haproxy, build OpenSSL with the no-shared
keyword and install it to a local directory, so your system is not affected :
@@ -100,6 +101,7 @@
When building haproxy, pass that path via SSL_INC and SSL_LIB to make and
include additional libs with ADDLIB if needed (in this case for example libdl):
+
$ make TARGET=linux26 USE_OPENSSL=1 SSL_INC=$STATICLIBSSL/include SSL_LIB=$STATICLIBSSL/lib ADDLIB=-ldl
It is also possible to include native support for ZLIB to benefit from HTTP
@@ -117,7 +119,7 @@
And I build it this way on OpenBSD or FreeBSD :
- $ make -f Makefile.bsd REGEX=pcre DEBUG= COPTS.generic="-Os -fomit-frame-pointer -mgnu"
+ $ gmake TARGET=freebsd USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1
And on a classic Linux with SSL and ZLIB support (eg: Red Hat 5.x) :
@@ -132,18 +134,46 @@
$ make TARGET=linux26 ARCH=i386 USE_OPENSSL=1 ADDLIB=-lz
-The BSD and OSX makefiles do not support build options for OpenSSL nor zlib.
-Also, at least on OpenBSD, pthread_mutexattr_setpshared() does not exist so
-the SSL session cache cannot be shared between multiple processes. If you want
-to enable these options, you need to use GNU make with the default makefile as
-follows :
-
- $ gmake TARGET=openbsd USE_OPENSSL=1 USE_ZLIB=1 USE_PRIVATE_CACHE=1
+The SSL stack supports session cache synchronization between all running
+processes. This involves some atomic operations and synchronization operations
+which come in multiple flavors depending on the system and architecture :
+
+ Atomic operations :
+ - internal assembler versions for x86/x86_64 architectures
+
+ - gcc builtins for other architectures. Some architectures might not
+ be fully supported or might require a more recent version of gcc.
+ If your architecture is not supported, you willy have to either use
+ pthread if supported, or to disable the shared cache.
+
+ - pthread (posix threads). Pthreads are very common but inter-process
+ support is not that common, and some older operating systems did not
+ report an error when enabling multi-process mode, so they used to
+ silently fail, possibly causing crashes. Linux's implementation is
+ fine. OpenBSD doesn't support them and doesn't build. FreeBSD 9 builds
+ and reports an error at runtime, while certain older versions might
+ silently fail. Pthreads are enabled using USE_PTHREAD_PSHARED=1.
+
+ Synchronization operations :
+ - internal spinlock : this mode is OS-independant, light but will not
+ scale well to many processes. However, accesses to the session cache
+ are rare enough that this mode could certainly always be used. This
+ is the default mode.
+
+ - Futexes, which are Linux-specific highly scalable light weight mutexes
+ implemented in user-space with some limited assistance from the kernel.
+ This is the default on Linux 2.6 and above and is enabled by passing
+ USE_FUTEX=1
+
+ - pthread (posix threads). See above.
+
+If none of these mechanisms is supported by your platform, you may need to
+build with USE_PRIVATE_CACHE=1 to totally disable SSL cache sharing. Then
+it is better not to run SSL on multiple processes.
If you need to pass other defines, includes, libraries, etc... then please
check the Makefile to see which ones will be available in your case, and
-use the USE_* variables in the GNU Makefile, or ADDINC, ADDLIB, and DEFINE
-variables in the BSD makefiles.
+use the USE_* variables in the Makefile.
AIX 5.3 is known to work with the generic target. However, for the binary to
also run on 5.2 or earlier, you need to build with DEFINE="-D_MSGQSUPPORT",
@@ -308,7 +338,7 @@
significant code contributions (features, fixes), but also time or money
donations :
- http://haproxy.1wt.eu/contrib.html
+ http://www.haproxy.org/contrib.html
Note to contributors: it's very handy when patches comes with a properly
formated subject. There are 3 criteria of particular importance in any patch :
@@ -496,12 +526,8 @@
definitely help you contribute quality code and take other people's feedback
in consideration. In order to clone the HAProxy Git repository :
- $ git clone http://git.1wt.eu/git/haproxy-1.4.git (stable 1.4)
- $ git clone http://git.1wt.eu/git/haproxy.git/ (development)
-
-The site above is slow, a faster mirror is maintained up to date here :
-
- $ git clone http://master.formilux.org/git/people/willy/haproxy.git/
+ $ git clone http://git.haproxy.org/git/haproxy-1.4.git (stable 1.4)
+ $ git clone http://git.haproxy.org/git/haproxy.git/ (development)
If you decide to use Git for your developments, then your commit messages will
have the subject line in the format described above, then the whole description
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/ROADMAP
^
|
@@ -1,4 +1,4 @@
-Medium-long term roadmap - 2014/04/23
+Medium-long term roadmap - 2014/05/10
Legend: '+' = done, '-' = todo, '*' = done except doc
@@ -14,8 +14,6 @@
- add the ability to only dump response errors to more easily detect
anomalies without being polluted with attacks in requests.
- - add support for server-side unix sockets
-
- have multi-criteria analysers which subscribe to req flags, rsp flags, and
stream interface changes. This would result in a single analyser to wait
for the end of data transfer in HTTP.
@@ -28,18 +26,12 @@
based on request matching. Each session will have one ebtree node to be
attached to whatever queue the session is waiting in.
- - half-closed timeouts ?
-
- add a flag in logs to indicate keep-alive requests ?
- make it possible to condition a timeout on an ACL (dynamic timeouts)
- forwardfor/originalto except with IPv6
- - have a callback function which would be called after a server is selected,
- for header post-processing. That would be mainly used to remove then add
- the server's name or cookie in a header so that the server knows it.
-
- remove lots of remaining Alert() calls or ensure that they forward to
send_log() after the fork.
@@ -50,6 +42,10 @@
- http-request track-sc* to avoid having the ugly "if !HTTP" in tcp-request
DONE:
+ * half-closed timeouts ?
+
+ * add support for server-side unix sockets
+
* server-side HTTP keepalive
=> maybe with limitation to only reuse connections that don't depend
on layer7 in a first time (just check the target).
@@ -195,6 +191,10 @@
- initcwnd parameter for bind sockets : needed in kernel first
+ - have a callback function which would be called after a server is selected,
+ for header post-processing. That would be mainly used to remove then add
+ the server's name or cookie in a header so that the server knows it.
+
Unsorted :
- outgoing log load-balancing (round-robin or hash among multiple servers)
- internal socket for "server XXX frontend:name"
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/SUBVERS
^
|
@@ -1,2 +1,2 @@
--8317b28
+-a339395
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/VERDATE
^
|
@@ -1,2 +1,2 @@
-2014-04-23 01:49:41 +0200
-2014/04/23
+2014-05-10 15:16:43 +0200
+2014/05/10
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/VERSION
^
|
@@ -1 +1 @@
-1.5-dev23
+1.5-dev25
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/doc/configuration.txt
^
|
@@ -4,7 +4,7 @@
----------------------
version 1.5
willy tarreau
- 2014/04/23
+ 2014/05/10
This document covers the configuration language as implemented in the version
@@ -69,11 +69,12 @@
7.1.6. Matching IPv4 and IPv6 addresses
7.2. Using ACLs to form conditions
7.3. Fetching samples
-7.3.1. Fetching samples from internal states
-7.3.2. Fetching samples at Layer 4
-7.3.3. Fetching samples at Layer 5
-7.3.4. Fetching samples from buffer contents (Layer 6)
-7.3.5. Fetching HTTP samples (Layer 7)
+7.3.1. Converters
+7.3.2. Fetching samples from internal states
+7.3.3. Fetching samples at Layer 4
+7.3.4. Fetching samples at Layer 5
+7.3.5. Fetching samples from buffer contents (Layer 6)
+7.3.6. Fetching HTTP samples (Layer 7)
7.4. Pre-defined ACLs
8. Logging
@@ -463,6 +464,7 @@
- unix-bind
* Performance tuning
+ - max-spread-checks
- maxconn
- maxconnrate
- maxcomprate
@@ -493,6 +495,7 @@
- tune.sndbuf.server
- tune.ssl.cachesize
- tune.ssl.lifetime
+ - tune.ssl.force-private-cache
- tune.ssl.maxrecord
- tune.zlib.memlevel
- tune.zlib.windowsize
@@ -522,16 +525,16 @@
On Linux 2.6 and above, it is possible to bind a process to a specific CPU
set. This means that the process will never run on other CPUs. The "cpu-map"
directive specifies CPU sets for process sets. The first argument is the
- process number to bind. This process must have a number between 1 and 32,
- and any process IDs above nbproc are ignored. It is possible to specify all
- processes at once using "all", only odd numbers using "odd" or even numbers
- using "even", just like with the "bind-process" directive. The second and
- forthcoming arguments are CPU sets. Each CPU set is either a unique number
- between 0 and 31 or a range with two such numbers delimited by a dash ('-').
- Multiple CPU numbers or ranges may be specified, and the processes will be
- allowed to bind to all of them. Obviously, multiple "cpu-map" directives may
- be specified. Each "cpu-map" directive will replace the previous ones when
- they overlap.
+ process number to bind. This process must have a number between 1 and 32 or
+ 64, depending on the machine's word size, and any process IDs above nbproc
+ are ignored. It is possible to specify all processes at once using "all",
+ only odd numbers using "odd" or even numbers using "even", just like with the
+ "bind-process" directive. The second and forthcoming arguments are CPU sets.
+ Each CPU set is either a unique number between 0 and 31 or 63 or a range with
+ two such numbers delimited by a dash ('-'). Multiple CPU numbers or ranges
+ may be specified, and the processes will be allowed to bind to all of them.
+ Obviously, multiple "cpu-map" directives may be specified. Each "cpu-map"
+ directive will replace the previous ones when they overlap.
crt-base <dir>
Assigns a default directory to fetch SSL certificates from when a relative
@@ -621,14 +624,16 @@
the "-p" command line argument. The file must be accessible to the user
starting the process. See also "daemon".
-stats bind-process [ all | odd | even | <number 1-32>[-<number 1-32>] ] ...
+stats bind-process [ all | odd | even | <number 1-64>[-<number 1-64>] ] ...
Limits the stats socket to a certain set of processes numbers. By default the
stats socket is bound to all processes, causing a warning to be emitted when
nbproc is greater than 1 because there is no way to select the target process
when connecting. However, by using this setting, it becomes possible to pin
the stats socket to a specific set of processes, typically the first one. The
warning will automatically be disabled when this setting is used, whatever
- the number of processes used.
+ the number of processes used. The maximum process ID depends on the machine's
+ word size (32 or 64). A better option consists in using the "process" setting
+ of the "stats socket" line to force the process on each line.
ssl-default-bind-ciphers <ciphers>
This setting is only available when support for OpenSSL was built in. It sets
@@ -719,6 +724,16 @@
3.2. Performance tuning
-----------------------
+max-spread-checks <delay in milliseconds>
+ By default, haproxy tries to spread the start of health checks across the
+ smallest health check interval of all the servers in a farm. The principle is
+ to avoid hammering services running on the same server. But when using large
+ check intervals (10 seconds or more), the last servers in the farm take some
+ time before starting to be tested, which can be a problem. This parameter is
+ used to enforce an upper bound on delay between the first and the last check,
+ even if the servers' check intervals are larger. When servers run with
+ shorter intervals, their intervals will be respected though.
+
maxconn <number>
Sets the maximum per-process number of concurrent connections to <number>. It
is equivalent to the command-line argument "-n". Proxies will stop accepting
@@ -972,6 +987,14 @@
and are shared between all processes if "nbproc" is greater than 1. Setting
this value to 0 disables the SSL session cache.
+tune.ssl.force-private-cache
+ This boolean disables SSL session cache sharing between all processes. It
+ should normally not be used since it will force many renegotiations due to
+ clients hitting a random process. But it may be required on some operating
+ systems where none of the SSL cache synchronization method may be used. In
+ this case, adding a first layer of hash-based load balancing before the SSL
+ layer might limit the impact of the lack of session sharing.
+
tune.ssl.lifetime <timeout>
Sets how long a cached SSL session may remain valid. This time is expressed
in seconds and defaults to 300 (5 min). It is important to understand that it
@@ -1255,14 +1278,11 @@
http-check send-state X - X X
http-request - X X X
http-response - X X X
-tcp-check connect - - X X
-tcp-check expect - - X X
-tcp-check send - - X X
-tcp-check send-binary - - X X
http-send-name-header - - X X
id - X X X
ignore-persist - X X X
log (*) X X X X
+max-keep-alive-queue X - X X
maxconn X X X -
mode X X X X
monitor fail - X X -
@@ -1366,6 +1386,10 @@
stick store-request - - X X
stick store-response - - X X
stick-table - - X X
+tcp-check connect - - X X
+tcp-check expect - - X X
+tcp-check send - - X X
+tcp-check send-binary - - X X
tcp-request connection - X X -
tcp-request content - X X X
tcp-request inspect-delay - X X X
@@ -1373,6 +1397,7 @@
tcp-response inspect-delay - - X X
timeout check X - X X
timeout client X X X -
+timeout client-fin X X X -
timeout clitimeout (deprecated) X X X -
timeout connect X - X X
timeout contimeout (deprecated) X - X X
@@ -1380,6 +1405,7 @@
timeout http-request X X X X
timeout queue X - X X
timeout server X - X X
+timeout server-fin X - X X
timeout srvtimeout (deprecated) X - X X
timeout tarpit X X X X
timeout tunnel X - X X
@@ -1738,6 +1764,7 @@
- 'ipv4@' -> address is always IPv4
- 'ipv6@' -> address is always IPv6
- 'unix@' -> address is a path to a local unix socket
+ - 'abns@' -> address is in abstract namespace (Linux only)
- 'fd@<n>' -> use file descriptor <n> inherited from the
parent. The fd must be bound and may or may not already
be listening.
@@ -1808,7 +1835,7 @@
documentation, and section 5 about bind options.
-bind-process [ all | odd | even | <number 1-32>[-<number 1-32>] ] ...
+bind-process [ all | odd | even | <number 1-64>[-<number 1-64>] ] ...
Limit visibility of an instance to a certain set of processes numbers.
May be used in sections : defaults | frontend | listen | backend
yes | yes | yes | yes
@@ -1816,19 +1843,20 @@
all All process will see this instance. This is the default. It
may be used to override a default value.
- odd This instance will be enabled on processes 1,3,5,...31. This
+ odd This instance will be enabled on processes 1,3,5,...63. This
option may be combined with other numbers.
- even This instance will be enabled on processes 2,4,6,...32. This
+ even This instance will be enabled on processes 2,4,6,...64. This
option may be combined with other numbers. Do not use it
with less than 2 processes otherwise some instances might be
missing from all processes.
number The instance will be enabled on this process number or range,
- whose values must all be between 1 and 32. You must be
- careful not to reference a process number greater than the
- configured global.nbproc, otherwise some instances might be
- missing from all processes.
+ whose values must all be between 1 and 32 or 64 depending on
+ the machine's word size. If a proxy is bound to process
+ numbers greater than the configured global.nbproc, it will
+ either be forced to process #1 if a single process was
+ specified, or to all processes otherwise.
This keyword limits binding of certain instances to certain processes. This
is useful in order not to have too many processes listening to the same
@@ -1836,10 +1864,13 @@
'nbproc 2' in the global section, then distributes the listeners among 'odd'
and 'even' instances.
- At the moment, it is not possible to reference more than 32 processes using
- this keyword, but this should be more than enough for most setups. Please
- note that 'all' really means all processes and is not limited to the first
- 32.
+ At the moment, it is not possible to reference more than 32 or 64 processes
+ using this keyword, but this should be more than enough for most setups.
+ Please note that 'all' really means all processes regardless of the machine's
+ word size, and is not limited to the first 32 or 64.
+
+ Each "bind" line may further be limited to a subset of the proxy's processes,
+ please consult the "process" bind keyword in section 5.1.
If some backends are referenced by frontends bound to other processes, the
backend automatically inherits the frontend's processes.
@@ -1861,7 +1892,7 @@
bind 10.0.0.4:80
bind-process 1-4
- See also : "nbproc" in global section.
+ See also : "nbproc" in global section, and "process" in section 5.1.
block { if | unless } <condition>
@@ -2835,7 +2866,12 @@
http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
add-header <name> <fmt> | set-header <name> <fmt> |
del-header <name> | set-nice <nice> | set-log-level <level> |
- set-tos <tos> | set-mark <mark> }
+ set-tos <tos> | set-mark <mark> |
+ add-acl(<file name>) <key fmt> |
+ del-acl(<file name>) <key fmt> |
+ del-map(<file name>) <key fmt> |
+ set-map(<file name>) <key fmt> <value fmt>
+ }
[ { if | unless } <condition> ]
Access control for Layer 7 requests
@@ -2931,6 +2967,39 @@
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
+ - "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the new entry. It
+ performs a lookup in the ACL before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "add acl" command from the
+ stats socket, but can be triggered by an HTTP request.
+
+ - "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It is the equivalent of the "del acl" command from the stats socket, but
+ can be triggered by an HTTP request.
+
+ - "del-map" is used to delete an entry from a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It takes one argument: "file name" It is the equivalent of the "del map"
+ command from the stats socket, but can be triggered by an HTTP request.
+
+ - "set-map" is used to add a new entry into a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes 2 arguments: <key fmt>,
+ which follows log-format rules, used to collect MAP key, and <value fmt>,
+ which follows log-format rules, used to collect content for the new entry.
+ It performs a lookup in the MAP before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "set map" command from the
+ stats socket, but can be triggered by an HTTP request.
+
There is no limit to the number of http-request statements per instance.
It is important to know that http-request rules are processed very early in
@@ -2963,12 +3032,37 @@
http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
http-request set-header X-SSL-Client-NotAfter %{+Q}[ssl_c_notafter]
+ Example:
+ acl key req.hdr(X-Add-Acl-Key) -m found
+ acl add path /addacl
+ acl del path /delacl
+
+ acl myhost hdr(Host) -f myhost.lst
+
+ http-request add-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key add
+ http-request del-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key del
+
+ Example:
+ acl value req.hdr(X-Value) -m found
+ acl setmap path /setmap
+ acl delmap path /delmap
+
+ use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
+
+ http-request set-map(map.lst) %[src] %[req.hdr(X-Value)] if setmap value
+ http-request del-map(map.lst) %[src] if delmap
+
See also : "stats http-request", section 3.4 about userlists and section 7
about ACL usage.
http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
set-header <name> <fmt> | del-header <name> |
- set-log-level <level> | set-mark <mark> | set-tos <tos> }
+ set-log-level <level> | set-mark <mark> | set-tos <tos> |
+ add-acl(<file name>) <key fmt> |
+ del-acl(<file name>) <key fmt> |
+ del-map(<file name>) <key fmt> |
+ set-map(<file name>) <key fmt> <value fmt>
+ }
[ { if | unless } <condition> ]
Access control for Layer 7 responses
@@ -3041,6 +3135,39 @@
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
+ - "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the new entry. It
+ performs a lookup in the ACL before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "add acl" command from the
+ stats socket, but can be triggered by an HTTP response.
+
+ - "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It is the equivalent of the "del acl" command from the stats socket, but
+ can be triggered by an HTTP response.
+
+ - "del-map" is used to delete an entry from a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It takes one argument: "file name" It is the equivalent of the "del map"
+ command from the stats socket, but can be triggered by an HTTP response.
+
+ - "set-map" is used to add a new entry into a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes 2 arguments: <key fmt>,
+ which follows log-format rules, used to collect MAP key, and <value fmt>,
+ which follows log-format rules, used to collect content for the new entry.
+ It performs a lookup in the MAP before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "set map" command from the
+ stats socket, but can be triggered by an HTTP response.
+
There is no limit to the number of http-response statements per instance.
It is important to know that http-response rules are processed very early in
@@ -3048,189 +3175,24 @@
added by "add-header"/"set-header" are visible by almost all further ACL
rules.
- See also : "http-request", section 3.4 about userlists and section 7 about
- ACL usage.
-
-
-tcp-check connect [params*]
- Opens a new connection
- May be used in sections: defaults | frontend | listen | backend
- no | no | yes | yes
-
- When an application lies on more than a single TCP port or when HAProxy
- load-balance many services in a single backend, it makes sense to probe all
- the services individually before considering a server as operational.
-
- When there are no TCP port configured on the server line neither server port
- directive, then the 'tcp-check connect port <port>' must be the first step
- of the sequence.
-
- In a tcp-check ruleset a 'connect' is required, it is also mandatory to start
- the ruleset with a 'connect' rule. Purpose is to ensure admin know what they
- do.
-
- Parameters :
- They are optional and can be used to describe how HAProxy should open and
- use the TCP connection.
-
- port if not set, check port or server port is used.
- It tells HAProxy where to open the connection to.
- <port> must be a valid TCP port source integer, from 1 to 65535.
-
- send-proxy send a PROXY protocol string
-
- ssl opens a ciphered connection
-
- Examples:
- # check HTTP and HTTPs services on a server.
- # first open port 80 thanks to server line port directive, then
- # tcp-check opens port 443, ciphered and run a request on it:
- option tcp-check
- tcp-check connect
- tcp-check send GET\ /\ HTTP/1.0\r\n
- tcp-check send Host:\ haproxy.1wt.eu\r\n
- tcp-check send \r\n
- tcp-check expect rstring (2..|3..)
- tcp-check connect port 443 ssl
- tcp-check send GET\ /\ HTTP/1.0\r\n
- tcp-check send Host:\ haproxy.1wt.eu\r\n
- tcp-check send \r\n
- tcp-check expect rstring (2..|3..)
- server www 10.0.0.1 check port 80
-
- # check both POP and IMAP from a single server:
- option tcp-check
- tcp-check connect port 110
- tcp-check expect string +OK\ POP3\ ready
- tcp-check connect port 143
- tcp-check expect string *\ OK\ IMAP4\ ready
- server mail 10.0.0.1 check
-
- See also : "option tcp-check", "tcp-check send", "tcp-check expect"
-
-
-tcp-check expect [!] <match> <pattern>
- Specify data to be collected and analysed during a generic health check
- May be used in sections: defaults | frontend | listen | backend
- no | no | yes | yes
-
- Arguments :
- <match> is a keyword indicating how to look for a specific pattern in the
- response. The keyword may be one of "string", "rstring" or
- binary.
- The keyword may be preceded by an exclamation mark ("!") to negate
- the match. Spaces are allowed between the exclamation mark and the
- keyword. See below for more details on the supported keywords.
-
- <pattern> is the pattern to look for. It may be a string or a regular
- expression. If the pattern contains spaces, they must be escaped
- with the usual backslash ('\').
- If the match is set to binary, then the pattern must be passed as
- a serie of hexadecimal digits in an even number. Each sequence of
- two digits will represent a byte. The hexadecimal digits may be
- used upper or lower case.
-
-
- The available matches are intentionally similar to their http-check cousins :
-
- string <string> : test the exact string matches in the response buffer.
- A health check response will be considered valid if the
- response's buffer contains this exact string. If the
- "string" keyword is prefixed with "!", then the response
- will be considered invalid if the body contains this
- string. This can be used to look for a mandatory pattern
- in a protocol response, or to detect a failure when a
- specific error appears in a protocol banner.
-
- rstring <regex> : test a regular expression on the response buffer.
- A health check response will be considered valid if the
- response's buffer matches this expression. If the
- "rstring" keyword is prefixed with "!", then the response
- will be considered invalid if the body matches the
- expression.
-
- binary <hexstring> : test the exact string in its hexadecimal form matches
- in the response buffer. A health check response will
- be considered valid if the response's buffer contains
- this exact hexadecimal string.
- Purpose is to match data on binary protocols.
-
- It is important to note that the responses will be limited to a certain size
- defined by the global "tune.chksize" option, which defaults to 16384 bytes.
- Thus, too large responses may not contain the mandatory pattern when using
- "string", "rstring" or binary. If a large response is absolutely required, it
- is possible to change the default max size by setting the global variable.
- However, it is worth keeping in mind that parsing very large responses can
- waste some CPU cycles, especially when regular expressions are used, and that
- it is always better to focus the checks on smaller resources. Also, in its
- current state, the check will not find any string nor regex past a null
- character in the response. Similarly it is not possible to request matching
- the null character.
-
- Examples :
- # perform a POP check
- option tcp-check
- tcp-check expect string +OK\ POP3\ ready
-
- # perform an IMAP check
- option tcp-check
- tcp-check expect string *\ OK\ IMAP4\ ready
-
- # look for the redis master server
- option tcp-check
- tcp-check send PING\r\n
- tcp-check expect +PONG
- tcp-check send info\ replication\r\n
- tcp-check expect string role:master
- tcp-check send QUIT\r\n
- tcp-check expect string +OK
-
-
- See also : "option tcp-check", "tcp-check connect", "tcp-check send",
- "tcp-check send-binary", "http-check expect", tune.chksize
-
-
-tcp-check send <data>
- Specify a string to be sent as a question during a generic health check
- May be used in sections: defaults | frontend | listen | backend
- no | no | yes | yes
-
- <data> : the data to be sent as a question during a generic health check
- session. For now, <data> must be a string.
-
- Examples :
- # look for the redis master server
- option tcp-check
- tcp-check send info\ replication\r\n
- tcp-check expect string role:master
+ Example:
+ acl key_acl res.hdr(X-Acl-Key) -m found
- See also : "option tcp-check", "tcp-check connect", "tcp-check expect",
- "tcp-check send-binary", tune.chksize
+ acl myhost hdr(Host) -f myhost.lst
+ http-response add-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
+ http-response del-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
-tcp-check send-binary <hexastring>
- Specify an hexa digits string to be sent as a binary question during a raw
- tcp health check
- May be used in sections: defaults | frontend | listen | backend
- no | no | yes | yes
-
- <data> : the data to be sent as a question during a generic health check
- session. For now, <data> must be a string.
- <hexastring> : test the exact string in its hexadecimal form matches in the
- response buffer. A health check response will be considered
- valid if the response's buffer contains this exact
- hexadecimal string.
- Purpose is to send binary data to ask on binary protocols.
+ Example:
+ acl value res.hdr(X-Value) -m found
- Examples :
- # redis check in binary
- option tcp-check
- tcp-check send-binary 50494e470d0a # PING\r\n
- tcp-check expect binary 2b504F4e47 # +PONG
+ use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
+ http-response set-map(map.lst) %[src] %[res.hdr(X-Value)] if value
+ http-response del-map(map.lst) %[src] if ! value
- See also : "option tcp-check", "tcp-check connect", "tcp-check expect",
- "tcp-check send", tune.chksize
+ See also : "http-request", section 3.4 about userlists and section 7 about
+ ACL usage.
http-send-name-header [<header>]
@@ -3367,6 +3329,34 @@
See also : Custom Log Format (8.2.4)
+max-keep-alive-queue <value>
+ Set the maximum server queue size for maintaining keep-alive connections
+ May be used in sections: defaults | frontend | listen | backend
+ yes | no | yes | yes
+
+ HTTP keep-alive tries to reuse the same server connection whenever possible,
+ but sometimes it can be counter-productive, for example if a server has a lot
+ of connections while other ones are idle. This is especially true for static
+ servers.
+
+ The purpose of this setting is to set a threshold on the number of queued
+ connections at which haproxy stops trying to reuse the same server and prefers
+ to find another one. The default value, -1, means there is no limit. A value
+ of zero means that keep-alive requests will never be queued. For very close
+ servers which can be reached with a low latency and which are not sensible to
+ breaking keep-alive, a low value is recommended (eg: local static server can
+ use a value of 10 or less). For remote servers suffering from a high latency,
+ higher values might be needed to cover for the latency and/or the cost of
+ picking a different server.
+
+ Note that this has no impact on responses which are maintained to the same
+ server consecutively to a 401 response. They will still go to the same server
+ even if they have to be queued.
+
+ See also : "option http-server-close", "option prefer-last-server", server
+ "maxconn" and cookie persistence.
+
+
maxconn <conns>
Fix the maximum number of concurrent connections on a frontend
May be used in sections : defaults | frontend | listen | backend
@@ -5763,6 +5753,7 @@
- 'ipv4@' -> address is always IPv4
- 'ipv6@' -> address is always IPv6
- 'unix@' -> address is a path to a local unix socket
+ - 'abns@' -> address is in abstract namespace (Linux only)
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
@@ -5809,6 +5800,7 @@
- 'ipv4@' -> address is always IPv4
- 'ipv6@' -> address is always IPv6
- 'unix@' -> address is a path to a local unix socket
+ - 'abns@' -> address is in abstract namespace (Linux only)
Any part of the address string may reference any number of
environment variables by preceding their name with a dollar
sign ('$') and optionally enclosing them with braces ('{}'),
@@ -5982,7 +5974,7 @@
stats admin { if | unless } <cond>
Enable statistics admin level if/unless a condition is matched
May be used in sections : defaults | frontend | listen | backend
- no | no | yes | yes
+ no | yes | yes | yes
This statement enables the statistics admin level if/unless a condition is
matched.
@@ -6035,7 +6027,7 @@
stats auth <user>:<passwd>
Enable statistics with authentication and grant access to an account
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments :
<user> is a user name to grant access to
@@ -6084,7 +6076,7 @@
stats enable
Enable statistics reporting with default settings
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments : none
This statement enables statistics reporting with default settings defined
@@ -6122,7 +6114,7 @@
stats hide-version
Enable statistics and hide HAProxy version reporting
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments : none
By default, the stats page reports some useful status information along with
@@ -6181,7 +6173,7 @@
stats realm <realm>
Enable statistics and set authentication realm
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments :
<realm> is the name of the HTTP Basic Authentication realm reported to
the browser. The browser uses it to display it in the pop-up
@@ -6221,7 +6213,7 @@
stats refresh <delay>
Enable statistics with automatic refresh
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments :
<delay> is the suggested refresh delay, specified in seconds, which will
be returned to the browser consulting the report page. While the
@@ -6263,7 +6255,7 @@
stats scope { <name> | "." }
Enable statistics and limit access scope
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments :
<name> is the name of a listen, frontend or backend section to be
reported. The special name "." (a single dot) designates the
@@ -6304,7 +6296,7 @@
stats show-desc [ <desc> ]
Enable reporting of a description on the statistics page.
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
<desc> is an optional description to be reported. If unspecified, the
description from global section is automatically used instead.
@@ -6329,6 +6321,11 @@
stats show-legends
+ Enable reporting additional information on the statistics page
+ May be used in sections : defaults | frontend | listen | backend
+ yes | yes | yes | yes
+ Arguments : none
+
Enable reporting additional information on the statistics page :
- cap: capabilities (proxy)
- mode: one of tcp, http or health (proxy)
@@ -6346,7 +6343,7 @@
stats show-node [ <name> ]
Enable reporting of a host name on the statistics page.
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments:
<name> is an optional name to be reported. If unspecified, the
node name from global section is automatically used instead.
@@ -6374,7 +6371,7 @@
stats uri <prefix>
Enable statistics and define the URI prefix to access them
May be used in sections : defaults | frontend | listen | backend
- yes | no | yes | yes
+ yes | yes | yes | yes
Arguments :
<prefix> is the prefix of any URI which will be redirected to stats. This
prefix may contain a question mark ('?') to indicate part of a
@@ -6948,6 +6945,187 @@
extraction.
+tcp-check connect [params*]
+ Opens a new connection
+ May be used in sections: defaults | frontend | listen | backend
+ no | no | yes | yes
+
+ When an application lies on more than a single TCP port or when HAProxy
+ load-balance many services in a single backend, it makes sense to probe all
+ the services individually before considering a server as operational.
+
+ When there are no TCP port configured on the server line neither server port
+ directive, then the 'tcp-check connect port <port>' must be the first step
+ of the sequence.
+
+ In a tcp-check ruleset a 'connect' is required, it is also mandatory to start
+ the ruleset with a 'connect' rule. Purpose is to ensure admin know what they
+ do.
+
+ Parameters :
+ They are optional and can be used to describe how HAProxy should open and
+ use the TCP connection.
+
+ port if not set, check port or server port is used.
+ It tells HAProxy where to open the connection to.
+ <port> must be a valid TCP port source integer, from 1 to 65535.
+
+ send-proxy send a PROXY protocol string
+
+ ssl opens a ciphered connection
+
+ Examples:
+ # check HTTP and HTTPs services on a server.
+ # first open port 80 thanks to server line port directive, then
+ # tcp-check opens port 443, ciphered and run a request on it:
+ option tcp-check
+ tcp-check connect
+ tcp-check send GET\ /\ HTTP/1.0\r\n
+ tcp-check send Host:\ haproxy.1wt.eu\r\n
+ tcp-check send \r\n
+ tcp-check expect rstring (2..|3..)
+ tcp-check connect port 443 ssl
+ tcp-check send GET\ /\ HTTP/1.0\r\n
+ tcp-check send Host:\ haproxy.1wt.eu\r\n
+ tcp-check send \r\n
+ tcp-check expect rstring (2..|3..)
+ server www 10.0.0.1 check port 80
+
+ # check both POP and IMAP from a single server:
+ option tcp-check
+ tcp-check connect port 110
+ tcp-check expect string +OK\ POP3\ ready
+ tcp-check connect port 143
+ tcp-check expect string *\ OK\ IMAP4\ ready
+ server mail 10.0.0.1 check
+
+ See also : "option tcp-check", "tcp-check send", "tcp-check expect"
+
+
+tcp-check expect [!] <match> <pattern>
+ Specify data to be collected and analysed during a generic health check
+ May be used in sections: defaults | frontend | listen | backend
+ no | no | yes | yes
+
+ Arguments :
+ <match> is a keyword indicating how to look for a specific pattern in the
+ response. The keyword may be one of "string", "rstring" or
+ binary.
+ The keyword may be preceded by an exclamation mark ("!") to negate
+ the match. Spaces are allowed between the exclamation mark and the
+ keyword. See below for more details on the supported keywords.
+
+ <pattern> is the pattern to look for. It may be a string or a regular
+ expression. If the pattern contains spaces, they must be escaped
+ with the usual backslash ('\').
+ If the match is set to binary, then the pattern must be passed as
+ a serie of hexadecimal digits in an even number. Each sequence of
+ two digits will represent a byte. The hexadecimal digits may be
+ used upper or lower case.
+
+
+ The available matches are intentionally similar to their http-check cousins :
+
+ string <string> : test the exact string matches in the response buffer.
+ A health check response will be considered valid if the
+ response's buffer contains this exact string. If the
+ "string" keyword is prefixed with "!", then the response
+ will be considered invalid if the body contains this
+ string. This can be used to look for a mandatory pattern
+ in a protocol response, or to detect a failure when a
+ specific error appears in a protocol banner.
+
+ rstring <regex> : test a regular expression on the response buffer.
+ A health check response will be considered valid if the
+ response's buffer matches this expression. If the
+ "rstring" keyword is prefixed with "!", then the response
+ will be considered invalid if the body matches the
+ expression.
+
+ binary <hexstring> : test the exact string in its hexadecimal form matches
+ in the response buffer. A health check response will
+ be considered valid if the response's buffer contains
+ this exact hexadecimal string.
+ Purpose is to match data on binary protocols.
+
+ It is important to note that the responses will be limited to a certain size
+ defined by the global "tune.chksize" option, which defaults to 16384 bytes.
+ Thus, too large responses may not contain the mandatory pattern when using
+ "string", "rstring" or binary. If a large response is absolutely required, it
+ is possible to change the default max size by setting the global variable.
+ However, it is worth keeping in mind that parsing very large responses can
+ waste some CPU cycles, especially when regular expressions are used, and that
+ it is always better to focus the checks on smaller resources. Also, in its
+ current state, the check will not find any string nor regex past a null
+ character in the response. Similarly it is not possible to request matching
+ the null character.
+
+ Examples :
+ # perform a POP check
+ option tcp-check
+ tcp-check expect string +OK\ POP3\ ready
+
+ # perform an IMAP check
+ option tcp-check
+ tcp-check expect string *\ OK\ IMAP4\ ready
+
+ # look for the redis master server
+ option tcp-check
+ tcp-check send PING\r\n
+ tcp-check expect +PONG
+ tcp-check send info\ replication\r\n
+ tcp-check expect string role:master
+ tcp-check send QUIT\r\n
+ tcp-check expect string +OK
+
+
+ See also : "option tcp-check", "tcp-check connect", "tcp-check send",
+ "tcp-check send-binary", "http-check expect", tune.chksize
+
+
+tcp-check send <data>
+ Specify a string to be sent as a question during a generic health check
+ May be used in sections: defaults | frontend | listen | backend
+ no | no | yes | yes
+
+ <data> : the data to be sent as a question during a generic health check
+ session. For now, <data> must be a string.
+
+ Examples :
+ # look for the redis master server
+ option tcp-check
+ tcp-check send info\ replication\r\n
+ tcp-check expect string role:master
+
+ See also : "option tcp-check", "tcp-check connect", "tcp-check expect",
+ "tcp-check send-binary", tune.chksize
+
+
+tcp-check send-binary <hexastring>
+ Specify an hexa digits string to be sent as a binary question during a raw
+ tcp health check
+ May be used in sections: defaults | frontend | listen | backend
+ no | no | yes | yes
+
+ <data> : the data to be sent as a question during a generic health check
+ session. For now, <data> must be a string.
+ <hexastring> : test the exact string in its hexadecimal form matches in the
+ response buffer. A health check response will be considered
+ valid if the response's buffer contains this exact
+ hexadecimal string.
+ Purpose is to send binary data to ask on binary protocols.
+
+ Examples :
+ # redis check in binary
+ option tcp-check
+ tcp-check send-binary 50494e470d0a # PING\r\n
+ tcp-check expect binary 2b504F4e47 # +PONG
+
+
+ See also : "option tcp-check", "tcp-check connect", "tcp-check expect",
+ "tcp-check send", tune.chksize
+
+
tcp-request connection <action> [{if | unless} <condition>]
Perform an action on an incoming connection depending on a layer 4 condition
May be used in sections : defaults | frontend | listen | backend
@@ -7372,7 +7550,8 @@
losses by specifying timeouts that are slightly above multiples of 3 seconds
(eg: 4 or 5 seconds). If some long-lived sessions are mixed with short-lived
sessions (eg: WebSocket and HTTP), it's worth considering "timeout tunnel",
- which overrides "timeout client" and "timeout server" for tunnels.
+ which overrides "timeout client" and "timeout server" for tunnels, as well as
+ "timeout client-fin" for half-closed connections.
This parameter is specific to frontends, but can be specified once for all in
"defaults" sections. This is in fact one of the easiest solutions not to
@@ -7388,6 +7567,31 @@
See also : "clitimeout", "timeout server", "timeout tunnel".
+timeout client-fin <timeout>
+ Set the inactivity timeout on the client side for half-closed connections.
+ May be used in sections : defaults | frontend | listen | backend
+ yes | yes | yes | no
+ Arguments :
+ <timeout> is the timeout value specified in milliseconds by default, but
+ can be in any other unit if the number is suffixed by the unit,
+ as explained at the top of this document.
+
+ The inactivity timeout applies when the client is expected to acknowledge or
+ send data while one direction is already shut down. This timeout is different
+ from "timeout client" in that it only applies to connections which are closed
+ in one direction. This is particularly useful to avoid keeping connections in
+ FIN_WAIT state for too long when clients do not disconnect cleanly. This
+ problem is particularly common long connections such as RDP or WebSocket.
+ Note that this timeout can override "timeout tunnel" when a connection shuts
+ down in one direction.
+
+ This parameter is specific to frontends, but can be specified once for all in
+ "defaults" sections. By default it is not set, so half-closed connections
+ will use the other timeouts (timeout.client or timeout.tunnel).
+
+ See also : "timeout client", "timeout server-fin", and "timeout tunnel".
+
+
timeout connect <timeout>
timeout contimeout <timeout> (deprecated)
Set the maximum time to wait for a connection attempt to a server to succeed.
@@ -7565,6 +7769,32 @@
See also : "srvtimeout", "timeout client" and "timeout tunnel".
+timeout server-fin <timeout>
+ Set the inactivity timeout on the server side for half-closed connections.
+ May be used in sections : defaults | frontend | listen | backend
+ yes | no | yes | yes
+ Arguments :
+ <timeout> is the timeout value specified in milliseconds by default, but
+ can be in any other unit if the number is suffixed by the unit,
+ as explained at the top of this document.
+
+ The inactivity timeout applies when the server is expected to acknowledge or
+ send data while one direction is already shut down. This timeout is different
+ from "timeout server" in that it only applies to connections which are closed
+ in one direction. This is particularly useful to avoid keeping connections in
+ FIN_WAIT state for too long when a remote server does not disconnect cleanly.
+ This problem is particularly common long connections such as RDP or WebSocket.
+ Note that this timeout can override "timeout tunnel" when a connection shuts
+ down in one direction. This setting was provided for completeness, but in most
+ situations, it should not be needed.
+
+ This parameter is specific to backends, but can be specified once for all in
+ "defaults" sections. By default it is not set, so half-closed connections
+ will use the other timeouts (timeout.server or timeout.tunnel).
+
+ See also : "timeout client-fin", "timeout server", and "timeout tunnel".
+
+
timeout tarpit <timeout>
Set the duration for which tarpitted connections will be maintained
May be used in sections : defaults | frontend | listen | backend
@@ -7606,6 +7836,14 @@
to a proxy), or after the first response when no keepalive/close option is
specified.
+ Since this timeout is usually used in conjunction with long-lived connections,
+ it usually is a good idea to also set "timeout client-fin" to handle the
+ situation where a client suddenly disappears from the net and does not
+ acknowledge a close, or sends a shutdown and does not acknowledge pending
+ data anymore. This can happen in lossy networks where firewalls are present,
+ and is detected by the presence of large amounts of sessions in a FIN_WAIT
+ state.
+
The value is specified in milliseconds by default, but can be in any other
unit if the number is suffixed by the unit, as specified at the top of this
document. Whatever the expected normal idle time, it is a good practice to
@@ -7621,11 +7859,11 @@
option http-server-close
timeout connect 5s
timeout client 30s
- timeout client 30s
+ timeout client-fin 30s
timeout server 30s
timeout tunnel 1h # timeout to use with WebSocket and CONNECT
- See also : "timeout client", "timeout server".
+ See also : "timeout client", "timeout client-fin", "timeout server".
transparent (deprecated)
@@ -8104,6 +8342,22 @@
enabled (check with haproxy -vv). Note that the NPN extension has been
replaced with the ALPN extension (see the "alpn" keyword).
+process [ all | odd | even | <number 1-64>[-<number 1-64>] ]
+ This restricts the list of processes on which this listener is allowed to
+ run. It does not enforce any process but eliminates those which do not match.
+ If the frontend uses a "bind-process" setting, the intersection between the
+ two is applied. If in the end the listener is not allowed to run on any
+ remaining process, a warning is emitted, and the listener will either run on
+ the first process of the listener if a single process was specified, or on
+ all of its processes if multiple processes were specified. For the unlikely
+ case where several ranges are needed, this directive may be repeated. The
+ main purpose of this directive is to be used with the stats sockets and have
+ one different socket per process. The second purpose is to have multiple bind
+ lines sharing the same IP:port but not the same process in a listener, so
+ that the system can distribute the incoming connections into multiple queues
+ and allow a smoother inter-process load balancing. Currently Linux 3.9 and
+ above is known for supporting this. See also "bind-process" and "nbproc".
+
ssl
This setting is only available when support for OpenSSL was built in. It
enables SSL deciphering on connections instantiated from this listener. A
@@ -8650,6 +8904,42 @@
Supported in default-server: No
+send-proxy-v2
+ The "send-proxy-v2" parameter enforces use of the PROXY protocol version 2
+ over any connection established to this server. The PROXY protocol informs
+ the other end about the layer 3/4 addresses of the incoming connection, so
+ that it can know the client's address or the public address it accessed to,
+ whatever the upper layer protocol. This setting must not be used if the
+ server isn't aware of this version of the protocol. See also the "send-proxy"
+ option of the "bind" keyword.
+
+ Supported in default-server: No
+
+send-proxy-v2-ssl
+ The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
+ 2 over any connection established to this server. The PROXY protocol informs
+ the other end about the layer 3/4 addresses of the incoming connection, so
+ that it can know the client's address or the public address it accessed to,
+ whatever the upper layer protocol. In addition, the SSL information extension
+ of the PROXY protocol is added to the PROXY protocol header. This setting
+ must not be used if the server isn't aware of this version of the protocol.
+ See also the "send-proxy-v2" option of the "bind" keyword.
+
+ Supported in default-server: No
+
+send-proxy-v2-ssl-cn
+ The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
+ 2 over any connection established to this server. The PROXY protocol informs
+ the other end about the layer 3/4 addresses of the incoming connection, so
+ that it can know the client's address or the public address it accessed to,
+ whatever the upper layer protocol. In addition, the SSL information extension
+ of the PROXY protocol, along along with the Common Name from the subject of
+ the client certificate (if any), is added to the PROXY protocol header. This
+ setting must not be used if the server isn't aware of this version of the
+ protocol. See also the "send-proxy-v2" option of the "bind" keyword.
+
+ Supported in default-server: No
+
slowstart <start_time_in_ms>
The "slowstart" parameter for a server accepts a value in milliseconds which
indicates after how long a server which has just come back up will run at
@@ -9359,6 +9649,10 @@
- name(arg1)
- name(arg1,arg2)
+
+7.3.1. Converters
+-----------------
+
Sample fetch methods may be combined with transformations to be applied on top
of the fetched sample (also called "converters"). These combinations form what
is called "sample expressions" and the result is a "sample". Initially this
@@ -9373,118 +9667,127 @@
The currently available list of transformation keywords include :
- lower Convert a string sample to lower case. This can only be placed
- after a string sample fetch function or after a transformation
- keyword returning a string type. The result is of type string.
-
- upper Convert a string sample to upper case. This can only be placed
- after a string sample fetch function or after a transformation
- keyword returning a string type. The result is of type string.
-
- hex Converts a binary input sample to an hex string containing two
- hex digits per input byte. It is used to log or transfer hex
- dumps of some binary input data in a way that can be reliably
- transferred (eg: an SSL ID can be copied in a header).
-
- ipmask(<mask>) Apply a mask to an IPv4 address, and use the result for
- lookups and storage. This can be used to make all hosts within
- a certain mask to share the same table entries and as such use
- the same server. The mask can be passed in dotted form (eg:
- 255.255.255.0) or in CIDR form (eg: 24).
-
- http_date([<offset>])
- Converts an integer supposed to contain a date since epoch to
- a string representing this date in a format suitable for use
- in HTTP header fields. If an offset value is specified, then
- it is a number of seconds that is added to the date before the
- conversion is operated. This is particularly useful to emit
- Date header fields, Expires values in responses when combined
- with a positive offset, or Last-Modified values when the
- offset is negative.
-
- language(<value[;value[;value[;...]]]>[,<default>])
- Returns the value with the highest q-factor from a list as
- extracted from the "accept-language" header using "req.fhdr".
- Values with no q-factor have a q-factor of 1. Values with a
- q-factor of 0 are dropped. Only values which belong to the
- list of semi-colon delimited <values> will be considered. If
- no value matches the given list and a default value is
- provided, it is returned. Note that language names may have
- a variant after a dash ('-'). If this variant is present in
- the list, it will be matched, but if it is not, only the base
- language is checked. The match is case-sensitive, and the
- output string is always one of those provided in arguments.
- The ordering of arguments is meaningless, only the ordering
- of the values in the request counts, as the first value among
- multiple sharing the same q-factor is used.
-
- Example :
-
- # this configuration switches to the backend matching a
- # given language based on the request :
-
- acl es req.fhdr(accept-language),language(es;fr;en) -m str es
- acl fr req.fhdr(accept-language),language(es;fr;en) -m str fr
- acl en req.fhdr(accept-language),language(es;fr;en) -m str en
- use_backend spanish if es
- use_backend french if fr
- use_backend english if en
- default_backend choose_your_language
-
- map(<map_file>[,<default_value>])
- map_<match_type>(<map_file>[,<default_value>])
- map_<match_type>_<output_type>(<map_file>[,<default_value>])
- Search the input value from <map_file> using the <match_type>
- matching method, and return the associated value converted to
- the type <output_type>. If the input value cannot be found in
- the <map_file>, the converter returns the <default_value>. If
- the <default_value> is not set, the converter fails and acts
- as if no input value could be fetched. If the <match_type> is
- not set, it defaults to "str". Likewise, if the <output_type>
- is not set, it defaults to "str". For convenience, the "map"
- keyword is an alias for "map_str" and maps a string to another
- string. The following array contains contains the list of all
- the map* converters.
-
- It is important to avoid overlapping between the keys : IP
- addresses and strings are stored in trees, so the first of the
- finest match will be used. Other keys are stored in lists, so
- the first matching occurrence will be used.
-
- +----+----------+---------+-------------+------------+
- | `-_ out | | | |
- | input `-_ | str | int | ip |
- | / match `-_ | | | |
- +---------------+---------+-------------+------------+
- | str / str | map_str | map_str_int | map_str_ip |
- | str / sub | map_sub | map_sub_int | map_sub_ip |
- | str / dir | map_dir | map_dir_int | map_dir_ip |
- | str / dom | map_dom | map_dom_int | map_dom_ip |
- | str / end | map_end | map_end_int | map_end_ip |
- | str / reg | map_reg | map_reg_int | map_reg_ip |
- | int / int | map_int | map_int_int | map_int_ip |
- | ip / ip | map_ip | map_ip_int | map_ip_ip |
- +---------------+---------+-------------+------------+
-
- The file contains one key + value per line. Lines which start
- with '#' are ignored, just like empty lines. Leading tabs and
- spaces are stripped. The key is then the first "word" (series
- of non-space/tabs characters), and the value is what follows
- this series of space/tab till the end of the line excluding
- trailing spaces/tabs.
-
- Example :
-
- # this is a comment and is ignored
- 2.22.246.0/23 United Kingdom \n
- <-><-----------><--><------------><---->
- | | | | `- trailing spaces ignored
- | | | `----------- value
- | | `--------------------- middle spaces ignored
- | `---------------------------- key
- `------------------------------------ leading spaces ignored
+base64
+ Converts a binary input sample to a base64 string. It is used to log or
+ transfer binary content in a way that can be reliably transferred (eg:
+ an SSL ID can be copied in a header).
+
+lower
+ Convert a string sample to lower case. This can only be placed after a string
+ sample fetch function or after a transformation keyword returning a string
+ type. The result is of type string.
+
+upper
+ Convert a string sample to upper case. This can only be placed after a string
+ sample fetch function or after a transformation keyword returning a string
+ type. The result is of type string.
+
+hex
+ Converts a binary input sample to an hex string containing two hex digits per
+ input byte. It is used to log or transfer hex dumps of some binary input data
+ in a way that can be reliably transferred (eg: an SSL ID can be copied in a
+ header).
+
+ipmask(<mask>)
+ Apply a mask to an IPv4 address, and use the result for lookups and storage.
+ This can be used to make all hosts within a certain mask to share the same
+ table entries and as such use the same server. The mask can be passed in
+ dotted form (eg: 255.255.255.0) or in CIDR form (eg: 24).
+
+http_date([<offset>])
+ Converts an integer supposed to contain a date since epoch to a string
+ representing this date in a format suitable for use in HTTP header fields. If
+ an offset value is specified, then it is a number of seconds that is added to
+ the date before the conversion is operated. This is particularly useful to
+ emit Date header fields, Expires values in responses when combined with a
+ positive offset, or Last-Modified values when the offset is negative.
+
+language(<value>[,<default>])
+ Returns the value with the highest q-factor from a list as extracted from the
+ "accept-language" header using "req.fhdr". Values with no q-factor have a
+ q-factor of 1. Values with a q-factor of 0 are dropped. Only values which
+ belong to the list of semi-colon delimited <values> will be considered. The
+ argument <value> syntax is "lang[;lang[;lang[;...]]]". If no value matches the
+ given list and a default value is provided, it is returned. Note that language
+ names may have a variant after a dash ('-'). If this variant is present in the
+ list, it will be matched, but if it is not, only the base language is checked.
+ The match is case-sensitive, and the output string is always one of those
+ provided in arguments. The ordering of arguments is meaningless, only the
+ ordering of the values in the request counts, as the first value among
+ multiple sharing the same q-factor is used.
+
+ Example :
+
+ # this configuration switches to the backend matching a
+ # given language based on the request :
+
+ acl es req.fhdr(accept-language),language(es;fr;en) -m str es
+ acl fr req.fhdr(accept-language),language(es;fr;en) -m str fr
+ acl en req.fhdr(accept-language),language(es;fr;en) -m str en
+ use_backend spanish if es
+ use_backend french if fr
+ use_backend english if en
+ default_backend choose_your_language
+
+map(<map_file>[,<default_value>])
+map_<match_type>(<map_file>[,<default_value>])
+map_<match_type>_<output_type>(<map_file>[,<default_value>])
+ Search the input value from <map_file> using the <match_type> matching method,
+ and return the associated value converted to the type <output_type>. If the
+ input value cannot be found in the <map_file>, the converter returns the
+ <default_value>. If the <default_value> is not set, the converter fails and
+ acts as if no input value could be fetched. If the <match_type> is not set, it
+ defaults to "str". Likewise, if the <output_type> is not set, it defaults to
+ "str". For convenience, the "map" keyword is an alias for "map_str" and maps a
+ string to another string.
+
+ It is important to avoid overlapping between the keys : IP addresses and
+ strings are stored in trees, so the first of the finest match will be used.
+ Other keys are stored in lists, so the first matching occurrence will be used.
+
+ The following array contains the list of all map functions avalaible sorted by
+ input type, match type and output type.
+
+ input type | match method | output type str | output type int | output type ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | str | map_str | map_str_int | map_str_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | beg | map_beg | map_beg_int | map_end_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | sub | map_sub | map_sub_int | map_sub_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | dir | map_dir | map_dir_int | map_dir_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | dom | map_dom | map_dom_int | map_dom_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | end | map_end | map_end_int | map_end_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ str | reg | map_reg | map_reg_int | map_reg_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ int | int | map_int | map_int_int | map_int_ip
+ -----------+--------------+-----------------+-----------------+---------------
+ ip | ip | map_ip | map_ip_int | map_ip_ip
+ -----------+--------------+-----------------+-----------------+---------------
+
+ The file contains one key + value per line. Lines which start with '#' are
+ ignored, just like empty lines. Leading tabs and spaces are stripped. The key
+ is then the first "word" (series of non-space/tabs characters), and the value
+ is what follows this series of space/tab till the end of the line excluding
+ trailing spaces/tabs.
+
+ Example :
+
+ # this is a comment and is ignored
+ 2.22.246.0/23 United Kingdom \n
+ <-><-----------><--><------------><---->
+ | | | | `- trailing spaces ignored
+ | | | `---------- value
+ | | `-------------------- middle spaces ignored
+ | `---------------------------- key
+ `------------------------------------ leading spaces ignored
-7.3.1. Fetching samples from internal states
+
+7.3.2. Fetching samples from internal states
--------------------------------------------
A first set of sample fetch methods applies to internal information which does
@@ -9692,7 +9995,7 @@
table_avl for other entry counting methods.
-7.3.2. Fetching samples at Layer 4
+7.3.3. Fetching samples at Layer 4
----------------------------------
The layer 4 usually describes just the transport layer which in haproxy is
@@ -10106,7 +10409,7 @@
debugging.
-7.3.3. Fetching samples at Layer 5
+7.3.4. Fetching samples at Layer 5
----------------------------------
The layer 5 usually describes just the session layer which in haproxy is
@@ -10115,6 +10418,37 @@
usable as low as the "tcp-request content" rule sets unless they require some
future information. Those generally include the results of SSL negotiations.
+ssl_bc : boolean
+ Returns true when the back connection was made via an SSL/TLS transport
+ layer and is locally deciphered. This means the outgoing connection was made
+ other a server with the "ssl" option.
+
+ssl_bc_alg_keysize : integer
+ Returns the symmetric cipher key size supported in bits when the outgoing
+ connection was made over an SSL/TLS transport layer.
+
+ssl_bc_cipher : string
+ Returns the name of the used cipher when the outgoing connection was made
+ over an SSL/TLS transport layer.
+
+ssl_bc_protocol : string
+ Returns the name of the used protocol when the outgoing connection was made
+ over an SSL/TLS transport layer.
+
+ssl_bc_unique_id : binary
+ When the outgoing connection was made over an SSL/TLS transport layer,
+ returns the TLS unique ID as defined in RFC5929 section 3. The unique id
+ can be encoded to base64 using the converter: "ssl_bc_unique_id,base64".
+
+ssl_bc_session_id : binary
+ Returns the SSL ID of the back connection when the outgoing connection was
+ made over an SSL/TLS transport layer. It is useful to log if we want to know
+ if session was reused or not.
+
+ssl_bc_use_keysize : integer
+ Returns the symmetric cipher key size used in bits when the outgoing
+ connection was made over an SSL/TLS transport layer.
+
ssl_c_ca_err : integer
When the incoming connection was made over an SSL/TLS transport layer,
returns the ID of the first error detected during verification of the client
@@ -10145,33 +10479,21 @@
For instance, "ssl_c_i_dn(OU,2)" the second organization unit, and
"ssl_c_i_dn(CN)" retrieves the common name.
- ACL derivatives :
- ssl_c_i_dn([<entry>[,<occ>]]) : exact string match
-
ssl_c_key_alg : string
Returns the name of the algorithm used to generate the key of the certificate
presented by the client when the incoming connection was made over an SSL/TLS
transport layer.
- ACL derivatives :
- ssl_c_key_alg : exact string match
-
ssl_c_notafter : string
Returns the end date presented by the client as a formatted string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
- ACL derivatives :
- ssl_c_notafter : exact string match
-
ssl_c_notbefore : string
Returns the start date presented by the client as a formatted string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
- ACL derivatives :
- ssl_c_notbefore : exact string match
-
ssl_c_s_dn([<entry>[,<occ>]]) : string
When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the subject of the certificate
@@ -10182,17 +10504,11 @@
For instance, "ssl_c_s_dn(OU,2)" the second organization unit, and
"ssl_c_s_dn(CN)" retrieves the common name.
- ACL derivatives :
- ssl_c_s_dn([<entry>[,<occ>]]) : exact string match
-
ssl_c_serial : binary
Returns the serial of the certificate presented by the client when the
incoming connection was made over an SSL/TLS transport layer. When used for
an ACL, the value(s) to match against can be passed in hexadecimal form.
- ACL derivatives :
- ssl_c_serial : hex block match
-
ssl_c_sha1 : binary
Returns the SHA-1 fingerprint of the certificate presented by the client when
the incoming connection was made over an SSL/TLS transport layer. This can be
@@ -10203,9 +10519,6 @@
the client when the incoming connection was made over an SSL/TLS transport
layer.
- ACL derivatives :
- ssl_c_sig_alg : exact string match
-
ssl_c_used : boolean
Returns true if current SSL session uses a client certificate even if current
connection uses SSL session resumption. See also "ssl_fc_has_crt".
@@ -10230,33 +10543,21 @@
For instance, "ssl_f_i_dn(OU,2)" the second organization unit, and
"ssl_f_i_dn(CN)" retrieves the common name.
- ACL derivatives :
- ssl_f_i_dn([<entry>[,<occ>]]) : exact string match
-
ssl_f_key_alg : string
Returns the name of the algorithm used to generate the key of the certificate
presented by the frontend when the incoming connection was made over an
SSL/TLS transport layer.
- ACL derivatives :
- ssl_f_key_alg : exact string match
-
ssl_f_notafter : string
Returns the end date presented by the frontend as a formatted string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
- ACL derivatives :
- ssl_f_notafter : exact string match
-
ssl_f_notbefore : string
Returns the start date presented by the frontend as a formatted string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
- ACL derivatives :
- ssl_f_notbefore : exact string match
-
ssl_f_s_dn([<entry>[,<occ>]]) : string
When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the subject of the certificate
@@ -10267,25 +10568,21 @@
For instance, "ssl_f_s_dn(OU,2)" the second organization unit, and
"ssl_f_s_dn(CN)" retrieves the common name.
- ACL derivatives :
- ssl_f_s_dn([<entry>[,<occ>]]) : exact string match
-
ssl_f_serial : binary
Returns the serial of the certificate presented by the frontend when the
incoming connection was made over an SSL/TLS transport layer. When used for
an ACL, the value(s) to match against can be passed in hexadecimal form.
- ACL derivatives :
- ssl_f_serial : hex block match
+ssl_f_sha1 : binary
+ Returns the SHA-1 fingerprint of the certificate presented by the frontend
+ when the incoming connection was made over an SSL/TLS transport layer. This
+ can be used to know which certificate was chosen using SNI.
ssl_f_sig_alg : string
Returns the name of the algorithm used to sign the certificate presented by
the frontend when the incoming connection was made over an SSL/TLS transport
layer.
- ACL derivatives :
- ssl_f_sig_alg : exact string match
-
ssl_f_version : integer
Returns the version of the certificate presented by the frontend when the
incoming connection was made over an SSL/TLS transport layer.
@@ -10317,16 +10614,10 @@
list, any other one may be requested. The TLS ALPN extension is meant to
replace the TLS NPN extension. See also "ssl_fc_npn".
- ACL derivatives :
- ssl_fc_alpn : exact string match
-
ssl_fc_cipher : string
Returns the name of the used cipher when the incoming connection was made
over an SSL/TLS transport layer.
- ACL derivatives :
- ssl_fc_cipher : exact string match
-
ssl_fc_has_crt : boolean
Returns true if a client certificate is present in an incoming connection over
SSL/TLS transport layer. Useful if 'verify' statement is set to 'optional'.
@@ -10352,20 +10643,14 @@
forces the client to pick a protocol from this list, any other one may be
requested. Please note that the TLS NPN extension was replaced with ALPN.
- ACL derivatives :
- ssl_fc_npn : exact string match
-
ssl_fc_protocol : string
Returns the name of the used protocol when the incoming connection was made
over an SSL/TLS transport layer.
- ACL derivatives :
- ssl_fc_protocol : exact string match
-
-ssl_fc_unique_id : string
+ssl_fc_unique_id : binary
When the incoming connection was made over an SSL/TLS transport layer,
- returns a base64 encoded string containing the TLS unique ID as defined
- in RFC5929 section 3.
+ returns the TLS unique ID as defined in RFC5929 section 3. The unique id
+ can be encoded to base64 using the converter: "ssl_bc_unique_id,base64".
ssl_fc_session_id : binary
Returns the SSL ID of the front connection when the incoming connection was
@@ -10387,7 +10672,6 @@
enabled (check haproxy -vv).
ACL derivatives :
- ssl_fc_sni : exact string match
ssl_fc_sni_end : suffix match
ssl_fc_sni_reg : regex match
@@ -10396,7 +10680,7 @@
connection was made over an SSL/TLS transport layer.
-7.3.4. Fetching samples from buffer contents (Layer 6)
+7.3.5. Fetching samples from buffer contents (Layer 6)
------------------------------------------------------
Fetching samples from buffer contents is a bit different from the previous
@@ -10625,7 +10909,7 @@
tcp-request content reject
-7.3.5. Fetching HTTP samples (Layer 7)
+7.3.6. Fetching HTTP samples (Layer 7)
--------------------------------------
It is possible to fetch samples from HTTP contents, requests and responses.
@@ -10686,12 +10970,22 @@
and "url", it can be used in both request and response because it's
allocated.
+capture.req.ver : string
+ This extracts the request's HTTP version and returns either "HTTP/1.0" or
+ "HTTP/1.1". Unlike "req.ver", it can be used in both request, response, and
+ logs because it relies on a persistent flag.
+
capture.res.hdr(<idx>) : string
This extracts the content of the header captured by the "capture response
header", idx is the position of the capture keyword in the configuration.
The first entry is an index of 0.
See also: "capture response header"
+capture.res.ver : string
+ This extracts the response's HTTP version and returns either "HTTP/1.0" or
+ "HTTP/1.1". Unlike "res.ver", it can be used in logs because it relies on a
+ persistent flag.
+
req.cook([<name>]) : string
cook([<name>]) : string (deprecated)
This extracts the last occurrence of the cookie name <name> on a "Cookie"
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/doc/proxy-protocol.txt
^
|
@@ -1,5 +1,5 @@
-2012/11/19 Willy Tarreau
- Exceliance
+2014/05/10 Willy Tarreau
+ HAProxy Technologies
The PROXY protocol
Versions 1 & 2
@@ -18,6 +18,7 @@
2011/03/20 - update: implementation and security considerations
2012/06/21 - add support for binary format
2012/11/19 - final review and fixes
+ 2014/05/18 - modify and extend PROXY protocol version 2
1. Background
@@ -326,25 +327,27 @@
Note that this block contains a null byte at the 5th position, so it must not
be handled as a null-terminated string.
-The next byte (the 13th one) is the protocol version. As of this specification,
-it must always be sent as \x02 and the receiver must only accept this value.
+The next byte (the 13th one) is the protocol version and command.
-The 14th byte represents the command :
- - \x00 : LOCAL : the connection was established on purpose by the proxy
+The highest four bits contains the version. As of this specification, it must
+always be sent as \x2 and the receiver must only accept this value.
+
+The lowest four bits represents the command :
+ - \x0 : LOCAL : the connection was established on purpose by the proxy
without being relayed. The connection endpoints are the sender and the
receiver. Such connections exist when the proxy sends health-checks to the
server. The receiver must accept this connection as valid and must use the
real connection endpoints and discard the protocol block including the
family which is ignored.
- - \x01 : PROXY : the connection was established on behalf of another node,
+ - \x1 : PROXY : the connection was established on behalf of another node,
and reflects the original connection endpoints. The receiver must then use
the information provided in the protocol block to get original the address.
- other values are unassigned and must not be emitted by senders. Receivers
must drop connections presenting unexpected values here.
-The 15th byte contains the transport protocol and address family. The highest 4
+The 14th byte contains the transport protocol and address family. The highest 4
bits contain the address family, the lowest 4 bits contain the protocol.
The address family maps to the original socket family without necessarily
@@ -370,7 +373,7 @@
- other values are unspecified and must not be emitted in version 2 of this
protocol and must be rejected as invalid by receivers.
-The transport protocol is specified in the lowest 4 bits of the the 15th byte :
+The transport protocol is specified in the lowest 4 bits of the the 14th byte :
- 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
or unsupported protocol. The sender should use this family when sending
@@ -424,11 +427,10 @@
to implement other ones, provided that it automatically falls back to the
UNSPEC mode for the valid combinations above that it does not support.
-The 16th byte is the address length in bytes. It is used so that the receiver
-knows how many address bytes to skip even when it does not implement the
-presented protocol. Thus the length of the protocol header in bytes is always
-exactly 16 + this byte. This means that the largest protocol header may only
-be 16 + 255 = 271 bytes, which fits in a usual MSS. When a sender presents a
+The 15th and 16th bytes is the address length in bytes in network endien order.
+It is used so that the receiver knows how many address bytes to skip even when
+it does not implement the presented protocol. Thus the length of the protocol
+header in bytes is always exactly 16 + this value. When a sender presents a
LOCAL connection, it should not present any address so it sets this field to
zero. Receivers MUST always consider this field to skip the appropriate number
of bytes and must not assume zero is presented for LOCAL connections. When a
@@ -439,10 +441,9 @@
struct proxy_hdr_v2 {
uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
- uint8_t ver; /* hex 02 */
- uint8_t cmd; /* hex 00 or 01 */
+ uint8_t ver; /* protocol version and command */
uint8_t fam; /* protocol family and address */
- uint8_t len; /* number of following bytes part of the header */
+ uint16_t len; /* number of following bytes part of the header */
};
Starting from the 17th byte, addresses are presented in network byte order.
@@ -499,6 +500,24 @@
- otherwise the protocol is not covered by this specification and the
connection must be dropped.
+If the length specified in the PROXY protocol header indicates that additional
+bytes are part of the header beyond the address information, a receiver may
+choose to skip over and ignore those bytes, or attempt to interpret those
+bytes.
+
+The information in those bytes will be arranged in Type-Length-Value (TLV
+vectors) in the following format. The first byte is the Type of the vector.
+The second two bytes represent the length in bytes of the value (not included
+the Type and Length bytes), and following the length field is the number of
+bytes specified by the length.
+
+ struct {
+ uint8_t type;
+ uint8_t length_hi;
+ uint8_t length_lo;
+ uint8_t value[0];
+ } tlv;
+
3. Implementations
@@ -516,6 +535,9 @@
"accept-proxy", then the relayed information is the one advertised in this
connection's PROXY line.
+ - Haproxy 1.5 also implements version 2 of the PROXY protocol as a sender. In
+ addition, a TLV with limited, optional, SSL information has been added.
+
Stunnel added support for version 1 of the protocol for outgoing connections in
version 4.45.
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/ebtree/ebmbtree.h
^
|
@@ -373,7 +373,9 @@
/* Find the first occurence of the longest prefix matching a key <x> in the
* tree <root>. It's the caller's responsibility to ensure that key <x> is at
- * least as long as the keys in the tree. If none can be found, return NULL.
+ * least as long as the keys in the tree. Note that this can be ensured by
+ * having a byte at the end of <x> which cannot be part of any prefix, typically
+ * the trailing zero for a string. If none can be found, return NULL.
*/
static forceinline struct ebmb_node *__ebmb_lookup_longest(struct eb_root *root, const void *x)
{
@@ -465,7 +467,9 @@
/* Find the first occurence of a prefix matching a key <x> of <pfx> BITS in the
* tree <root>. It's the caller's responsibility to ensure that key <x> is at
- * least as long as the keys in the tree. If none can be found, return NULL.
+ * least as long as the keys in the tree. Note that this can be ensured by
+ * having a byte at the end of <x> which cannot be part of any prefix, typically
+ * the trailing zero for a string. If none can be found, return NULL.
*/
static forceinline struct ebmb_node *__ebmb_lookup_prefix(struct eb_root *root, const void *x, unsigned int pfx)
{
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/examples/haproxy.spec
^
|
@@ -1,6 +1,6 @@
Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments
Name: haproxy
-Version: 1.5-dev23
+Version: 1.5-dev25
Release: 1
License: GPL
Group: System Environment/Daemons
@@ -76,6 +76,12 @@
%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
%changelog
+* Sat May 10 2014 Willy Tarreau <w@1wt.eu>
+- updated to 1.5-dev25
+
+* Sat Apr 26 2014 Willy Tarreau <w@1wt.eu>
+- updated to 1.5-dev24
+
* Wed Apr 23 2014 Willy Tarreau <w@1wt.eu>
- updated to 1.5-dev23
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/accept4.h
^
|
@@ -58,7 +58,7 @@
return socketcall(SYS_ACCEPT4, args);
}
#else
-static _syscall4(int, accept4, int, sockfd, struct sockaddr *, addr, socklen_t *, addrlen, int, flags);
+static inline _syscall4(int, accept4, int, sockfd, struct sockaddr *, addr, socklen_t *, addrlen, int, flags);
#endif /* VSYSCALL etc... */
#endif /* USE_MY_ACCEPT4 */
#endif /* __linux__ && USE_ACCEPT4 */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/buffer.h
^
|
@@ -228,29 +228,6 @@
return right - left;
}
-/* Return the amount of bytes that can be written into the buffer at once,
- * excluding the amount of reserved space passed in <res>, which is
- * preserved.
- */
-static inline int buffer_contig_space_with_res(const struct buffer *buf, int res)
-{
- /* Proceed differently if the buffer is full, partially used or empty.
- * The hard situation is when it's partially used and either data or
- * reserved space wraps at the end.
- */
- int spare = buf->size - res;
-
- if (buffer_len(buf) >= spare)
- spare = 0;
- else if (buffer_len(buf)) {
- spare = buffer_contig_space(buf) - res;
- if (spare < 0)
- spare = 0;
- }
- return spare;
-}
-
-
/* Normalizes a pointer which is supposed to be relative to the beginning of a
* buffer, so that wrapping is correctly handled. The intent is to use this
* when increasing a pointer. Note that the wrapping test is only performed
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/splice.h
^
|
@@ -62,7 +62,7 @@
#define __NR_splice 313
#endif /* __NR_splice */
-static _syscall6(int, splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_out, size_t, len, unsigned long, flags);
+static inline _syscall6(int, splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_out, size_t, len, unsigned long, flags);
#endif /* VSYSCALL */
#else
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/standard.h
^
|
@@ -42,6 +42,10 @@
# define ULLONG_MAX (LLONG_MAX * 2ULL + 1)
#endif
+#ifndef LONGBITS
+#define LONGBITS ((unsigned int)sizeof(long) * 8)
+#endif
+
/* size used for max length of decimal representation of long long int. */
#define NB_LLMAX_STR (sizeof("-9223372036854775807")-1)
@@ -527,7 +531,7 @@
}
/* Simple popcount implementation. It returns the number of ones in a word */
-static inline unsigned int popcount(unsigned int a)
+static inline unsigned int popcount(unsigned long a)
{
unsigned int cnt;
for (cnt = 0; a; a >>= 1) {
@@ -537,6 +541,15 @@
return cnt;
}
+/* Build a word with the <bits> lower bits set (reverse of popcount) */
+static inline unsigned long nbits(int bits)
+{
+ if (--bits < 0)
+ return 0;
+ else
+ return (2UL << bits) - 1;
+}
+
/*
* Parse binary string written in hexadecimal (source) and store the decoded
* result into binstr and set binstrlen to the lengh of binstr. Memory for
@@ -614,7 +627,7 @@
/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address,
* otherwise zero.
*/
-static inline int is_addr(struct sockaddr_storage *addr)
+static inline int is_inet_addr(const struct sockaddr_storage *addr)
{
int i;
@@ -629,6 +642,17 @@
return 0;
}
+/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address,
+ * or is a unix address, otherwise returns zero.
+ */
+static inline int is_addr(const struct sockaddr_storage *addr)
+{
+ if (addr->ss_family == AF_UNIX)
+ return 1;
+ else
+ return is_inet_addr(addr);
+}
+
/* returns port in network byte order */
static inline int get_net_port(struct sockaddr_storage *addr)
{
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/syscall.h
^
|
@@ -37,42 +37,42 @@
*/
#ifndef _syscall1
#define _syscall1(tr, nr, t1, n1) \
- inline tr nr(t1 n1) { \
+ tr nr(t1 n1) { \
return syscall(__NR_##nr, n1); \
}
#endif
#ifndef _syscall2
#define _syscall2(tr, nr, t1, n1, t2, n2) \
- inline tr nr(t1 n1, t2 n2) { \
+ tr nr(t1 n1, t2 n2) { \
return syscall(__NR_##nr, n1, n2); \
}
#endif
#ifndef _syscall3
#define _syscall3(tr, nr, t1, n1, t2, n2, t3, n3) \
- inline tr nr(t1 n1, t2 n2, t3 n3) { \
+ tr nr(t1 n1, t2 n2, t3 n3) { \
return syscall(__NR_##nr, n1, n2, n3); \
}
#endif
#ifndef _syscall4
#define _syscall4(tr, nr, t1, n1, t2, n2, t3, n3, t4, n4) \
- inline tr nr(t1 n1, t2 n2, t3 n3, t4 n4) { \
+ tr nr(t1 n1, t2 n2, t3 n3, t4 n4) { \
return syscall(__NR_##nr, n1, n2, n3, n4); \
}
#endif
#ifndef _syscall5
#define _syscall5(tr, nr, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5) \
- inline tr nr(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5) { \
+ tr nr(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5) { \
return syscall(__NR_##nr, n1, n2, n3, n4, n5); \
}
#endif
#ifndef _syscall6
#define _syscall6(tr, nr, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \
- inline tr nr(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6) { \
+ tr nr(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6) { \
return syscall(__NR_##nr, n1, n2, n3, n4, n5, n6); \
}
#endif
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/common/version.h
^
|
@@ -39,19 +39,19 @@
#ifdef CONFIG_PRODUCT_URL
#define PRODUCT_URL CONFIG_PRODUCT_URL
#else
-#define PRODUCT_URL "http://haproxy.1wt.eu/"
+#define PRODUCT_URL "http://www.haproxy.org/"
#endif
#ifdef CONFIG_PRODUCT_URL_UPD
#define PRODUCT_URL_UPD CONFIG_PRODUCT_URL_UPD
#else
-#define PRODUCT_URL_UPD "http://haproxy.1wt.eu/#down"
+#define PRODUCT_URL_UPD "http://www.haproxy.org/#down"
#endif
#ifdef CONFIG_PRODUCT_URL_DOC
#define PRODUCT_URL_DOC CONFIG_PRODUCT_URL_DOC
#else
-#define PRODUCT_URL_DOC "http://haproxy.1wt.eu/#docs"
+#define PRODUCT_URL_DOC "http://www.haproxy.org/#docs"
#endif
#ifdef CONFIG_HAPROXY_VERSION
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/channel.h
^
|
@@ -280,14 +280,6 @@
return chn->buf->size - buffer_reserved(chn);
}
-/* Return the amount of bytes that can be written into the buffer at once,
- * excluding reserved space, which is preserved.
- */
-static inline int buffer_contig_space_res(const struct channel *chn)
-{
- return buffer_contig_space_with_res(chn->buf, buffer_reserved(chn));
-}
-
/* Returns the amount of space available at the input of the buffer, taking the
* reserved space into account if ->to_forward indicates that an end of transfer
* is close to happen. The test is optimized to avoid as many operations as
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/connection.h
^
|
@@ -41,7 +41,9 @@
/* receive a PROXY protocol header over a connection */
int conn_recv_proxy(struct connection *conn, int flag);
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote);
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote);
/* returns true is the transport layer is ready */
static inline int conn_xprt_ready(const struct connection *conn)
@@ -581,6 +583,8 @@
case CO_ER_SSL_CA_FAIL: return "SSL client CA chain cannot be verified";
case CO_ER_SSL_CRT_FAIL: return "SSL client certificate not trusted";
case CO_ER_SSL_HANDSHAKE: return "SSL handshake failure";
+ case CO_ER_SSL_HANDSHAKE_HB: return "SSL handshake failure after heartbeat";
+ case CO_ER_SSL_KILLED_HB: return "Stopped a TLSv1 heartbeat attack (CVE-2014-0160)";
case CO_ER_SSL_NO_TARGET: return "Attempt to use SSL on an unknown target (internal error)";
}
return NULL;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/dumpstats.h
^
|
@@ -31,6 +31,7 @@
#define STAT_HIDE_DOWN 0x00000008 /* hide 'down' servers in the stats page */
#define STAT_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */
#define STAT_ADMIN 0x00000020 /* indicate a stats admin level */
+#define STAT_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */
#define STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */
#define STATS_TYPE_FE 0
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/pattern.h
^
|
@@ -30,7 +30,7 @@
/* pattern management function arrays */
extern char *pat_match_names[PAT_MATCH_NUM];
-extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, char **);
+extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, int, char **);
extern int (*pat_index_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct pattern *, char **);
extern void (*pat_delete_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct pat_ref_elt *);
extern void (*pat_prune_fcts[PAT_MATCH_NUM])(struct pattern_expr *);
@@ -69,6 +69,7 @@
int pat_idx_list_reg(struct pattern_expr *expr, struct pattern *pat, char **err);
int pat_idx_tree_ip(struct pattern_expr *expr, struct pattern *pat, char **err);
int pat_idx_tree_str(struct pattern_expr *expr, struct pattern *pat, char **err);
+int pat_idx_tree_pfx(struct pattern_expr *expr, struct pattern *pat, char **err);
/*
*
@@ -101,34 +102,34 @@
/* ignore the current line */
-int pat_parse_nothing(const char *text, struct pattern *pattern, char **err);
+int pat_parse_nothing(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an integer. It is put both in min and max. */
-int pat_parse_int(const char *text, struct pattern *pattern, char **err);
+int pat_parse_int(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an version. It is put both in min and max. */
-int pat_parse_dotted_ver(const char *text, struct pattern *pattern, char **err);
+int pat_parse_dotted_ver(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a range of integers delimited by either ':' or '-'. If only one
* integer is read, it is set as both min and max.
*/
-int pat_parse_range(const char *text, struct pattern *pattern, char **err);
+int pat_parse_range(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a string. It is allocated and duplicated. */
-int pat_parse_str(const char *text, struct pattern *pattern, char **err);
+int pat_parse_str(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a hexa binary definition. It is allocated and duplicated. */
-int pat_parse_bin(const char *text, struct pattern *pattern, char **err);
+int pat_parse_bin(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a regex. It is allocated. */
-int pat_parse_reg(const char *text, struct pattern *pattern, char **err);
+int pat_parse_reg(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an IP address and an optional mask in the form addr[/mask].
* The addr may either be an IPv4 address or a hostname. The mask
* may either be a dotted mask or a number of bits. Returns 1 if OK,
* otherwise 0.
*/
-int pat_parse_ip(const char *text, struct pattern *pattern, char **err);
+int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **err);
/* NB: For two strings to be identical, it is required that their lengths match */
struct pattern *pat_match_str(struct sample *smp, struct pattern_expr *expr, int fill);
@@ -181,6 +182,7 @@
struct pat_ref *pat_ref_lookupid(int unique_id);
struct pat_ref *pat_ref_new(const char *reference, const char *display, unsigned int flags);
struct pat_ref *pat_ref_newid(int unique_id, const char *display, unsigned int flags);
+struct pat_ref_elt *pat_ref_find_elt(struct pat_ref *ref, const char *key);
int pat_ref_append(struct pat_ref *ref, char *pattern, char *sample, int line);
int pat_ref_add(struct pat_ref *ref, const char *pattern, const char *sample, char **err);
int pat_ref_set(struct pat_ref *ref, const char *pattern, const char *sample, char **err);
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/proto_http.h
^
|
@@ -63,30 +63,30 @@
#define HTTP_IS_TOKEN(x) (http_is_token[(unsigned char)(x)])
#define HTTP_IS_VER_TOKEN(x) (http_is_ver_token[(unsigned char)(x)])
-int process_cli(struct session *t);
-int process_srv_data(struct session *t);
-int process_srv_conn(struct session *t);
+int process_cli(struct session *s);
+int process_srv_data(struct session *s);
+int process_srv_conn(struct session *s);
int http_wait_for_request(struct session *s, struct channel *req, int an_bit);
int http_process_req_common(struct session *s, struct channel *req, int an_bit, struct proxy *px);
-int http_process_request(struct session *t, struct channel *req, int an_bit);
+int http_process_request(struct session *s, struct channel *req, int an_bit);
int http_process_tarpit(struct session *s, struct channel *req, int an_bit);
int http_wait_for_request_body(struct session *s, struct channel *req, int an_bit);
int http_send_name_header(struct http_txn *txn, struct proxy* be, const char* svr_name);
int http_wait_for_response(struct session *s, struct channel *rep, int an_bit);
-int http_process_res_common(struct session *t, struct channel *rep, int an_bit, struct proxy *px);
+int http_process_res_common(struct session *s, struct channel *rep, int an_bit, struct proxy *px);
int http_request_forward_body(struct session *s, struct channel *req, int an_bit);
int http_response_forward_body(struct session *s, struct channel *res, int an_bit);
-void debug_hdr(const char *dir, struct session *t, const char *start, const char *end);
-void get_srv_from_appsession(struct session *t, const char *begin, int len);
-int apply_filter_to_req_headers(struct session *t, struct channel *req, struct hdr_exp *exp);
-int apply_filter_to_req_line(struct session *t, struct channel *req, struct hdr_exp *exp);
+void debug_hdr(const char *dir, struct session *s, const char *start, const char *end);
+void get_srv_from_appsession(struct session *s, const char *begin, int len);
+int apply_filter_to_req_headers(struct session *s, struct channel *req, struct hdr_exp *exp);
+int apply_filter_to_req_line(struct session *s, struct channel *req, struct hdr_exp *exp);
int apply_filters_to_request(struct session *s, struct channel *req, struct proxy *px);
-int apply_filters_to_response(struct session *t, struct channel *rtr, struct proxy *px);
-void manage_client_side_appsession(struct session *t, const char *buf, int len);
-void manage_client_side_cookies(struct session *t, struct channel *req);
-void manage_server_side_cookies(struct session *t, struct channel *rtr);
-void check_response_for_cacheability(struct session *t, struct channel *rtr);
+int apply_filters_to_response(struct session *s, struct channel *rtr, struct proxy *px);
+void manage_client_side_appsession(struct session *s, const char *buf, int len);
+void manage_client_side_cookies(struct session *s, struct channel *req);
+void manage_server_side_cookies(struct session *s, struct channel *rtr);
+void check_response_for_cacheability(struct session *s, struct channel *rtr);
int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct proxy *backend);
void init_proto_http();
int http_find_full_header2(const char *name, int len,
@@ -119,9 +119,25 @@
struct chunk *http_error_message(struct session *s, int msgnum);
struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
const char **args, char **errmsg, int use_fmt);
+int smp_fetch_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+ const struct arg *args, struct sample *smp, const char *kw);
enum http_meth_t find_http_meth(const char *str, const int len);
+struct http_req_action_kw *action_http_req_custom(const char *kw);
+struct http_res_action_kw *action_http_res_custom(const char *kw);
+
+static inline void http_req_keywords_register(struct http_req_action_kw_list *kw_list)
+{
+ LIST_ADDQ(&http_req_keywords.list, &kw_list->list);
+}
+
+static inline void http_res_keywords_register(struct http_res_action_kw_list *kw_list)
+{
+ LIST_ADDQ(&http_res_keywords.list, &kw_list->list);
+}
+
+
/* to be used when contents change in an HTTP message */
#define http_msg_move_end(msg, bytes) do { \
unsigned int _bytes = (bytes); \
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/queue.h
^
|
@@ -65,6 +65,11 @@
return LIST_ELEM(px->pendconns.n, struct pendconn *, list);
}
+/* Returns 0 if all slots are full on a server, or 1 if there are slots available. */
+static inline int server_has_room(const struct server *s) {
+ return !s->maxconn || s->cur_sess < srv_dynamic_maxconn(s);
+}
+
/* returns 0 if nothing has to be done for server <s> regarding queued connections,
* and non-zero otherwise. If the server is down, we only check its own queue. Suited
* for and if/else usage.
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/shctx.h
^
|
@@ -28,6 +28,9 @@
#define SHCTX_APPNAME "haproxy"
#endif
+#define SHCTX_E_ALLOC_CACHE -1
+#define SHCTX_E_INIT_LOCK -2
+
/* Allocate shared memory context.
* <size> is the number of allocated blocks into cache (default 128 bytes)
* A block is large enough to contain a classic session (without client cert)
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/proto/ssl_sock.h
^
|
@@ -32,6 +32,16 @@
extern int sslconns;
extern int totalsslconns;
+/* boolean, returns true if connection is over SSL */
+static inline
+int ssl_sock_is_ssl(struct connection *conn)
+{
+ if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+ return 0;
+ else
+ return 1;
+}
+
int ssl_sock_handshake(struct connection *conn, unsigned int flag);
int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *proxy);
void ssl_sock_free_certs(struct bind_conf *bind_conf);
@@ -40,6 +50,10 @@
void ssl_sock_free_all_ctx(struct bind_conf *bind_conf);
const char *ssl_sock_get_cipher_name(struct connection *conn);
const char *ssl_sock_get_proto_version(struct connection *conn);
+char *ssl_sock_get_version(struct connection *conn);
+int ssl_sock_get_cert_used(struct connection *conn);
+char *ssl_sock_get_common_name(struct connection *conn);
+unsigned int ssl_sock_get_verify_result(struct connection *conn);
#endif /* _PROTO_SSL_SOCK_H */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/acl.h
^
|
@@ -93,7 +93,7 @@
const char *kw;
char *fetch_kw;
int match_type; /* Contain PAT_MATCH_* */
- int (*parse)(const char *text, struct pattern *pattern, char **err);
+ int (*parse)(const char *text, struct pattern *pattern, int flags, char **err);
int (*index)(struct pattern_expr *expr, struct pattern *pattern, char **err);
void (*delete)(struct pattern_expr *expr, struct pat_ref_elt *);
void (*prune)(struct pattern_expr *expr);
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/channel.h
^
|
@@ -59,7 +59,7 @@
#define CF_READ_ERROR 0x00000008 /* unrecoverable error on producer side */
#define CF_READ_ACTIVITY (CF_READ_NULL|CF_READ_PARTIAL|CF_READ_ERROR)
-/* unused: 0x00000010 */
+#define CF_WAKE_CONNECT 0x00000010 /* wake the task up after connect succeeds */
#define CF_SHUTR 0x00000020 /* producer has already shut down */
#define CF_SHUTR_NOW 0x00000040 /* the producer must shut down for reads ASAP */
#define CF_READ_NOEXP 0x00000080 /* producer should not expire */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/connection.h
^
|
@@ -162,7 +162,9 @@
CO_ER_SSL_CA_FAIL, /* client cert verification failed in the CA chain */
CO_ER_SSL_CRT_FAIL, /* client cert verification failed on the certificate */
CO_ER_SSL_HANDSHAKE, /* SSL error during handshake */
- CO_ER_SSL_NO_TARGET, /* unkonwn target (not client nor server) */
+ CO_ER_SSL_HANDSHAKE_HB, /* SSL error during handshake with heartbeat present */
+ CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */
+ CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */
};
/* source address settings for outgoing connections */
@@ -265,6 +267,77 @@
} addr; /* addresses of the remote side, client for producer and server for consumer */
};
+/* proxy protocol v2 definitions */
+#define PP2_SIGNATURE_LEN 12
+#define PP2_HEADER_LEN 16
+#define PP2_VERSION 0x20
+#define PP2_CMD_LOCAL 0x00
+#define PP2_CMD_PROXY 0x01
+#define PP2_FAM_UNSPEC 0x00
+#define PP2_FAM_INET 0x10
+#define PP2_FAM_INET6 0x20
+#define PP2_FAM_UNIX 0x30
+#define PP2_TRANS_UNSPEC 0x00
+#define PP2_TRANS_STREAM 0x01
+#define PP2_TRANS_DGRAM 0x02
+
+#define PP2_ADDR_LEN_UNSPEC 0
+#define PP2_ADDR_LEN_INET 12
+#define PP2_ADDR_LEN_INET6 36
+#define PP2_ADDR_LEN_UNIX 216
+
+#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
+#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
+#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
+#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
+
+struct proxy_hdr_v2 {
+ uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ uint8_t cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and transport */
+ uint16_t len; /* number of following bytes part of the header */
+};
+
+union proxy_addr {
+ struct { /* for TCP/UDP over IPv4, len = 12 */
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv4_addr;
+ struct { /* for TCP/UDP over IPv6, len = 36 */
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv6_addr;
+ struct { /* for AF_UNIX sockets, len = 216 */
+ uint8_t src_addr[108];
+ uint8_t dst_addr[108];
+ } unix_addr;
+};
+
+#define PP2_TYPE_SSL 0x20
+#define PP2_TYPE_SSL_VERSION 0x21
+#define PP2_TYPE_SSL_CN 0x22
+
+struct tlv {
+ uint8_t type;
+ uint8_t length_hi;
+ uint8_t length_lo;
+ uint8_t value[0];
+}__attribute__((packed));
+
+struct tlv_ssl {
+ struct tlv tlv;
+ uint8_t client;
+ uint32_t verify;
+ uint8_t sub_tlv[0];
+}__attribute__((packed));
+
+#define PP2_CLIENT_SSL 0x01
+#define PP2_CLIENT_CERT 0x02
+
#endif /* _TYPES_CONNECTION_H */
/*
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/global.h
^
|
@@ -105,6 +105,7 @@
unsigned int req_count; /* request counter (HTTP or TCP session) for logs and unique_id */
int last_checks;
int spread_checks;
+ int max_spread_checks;
char *chroot;
char *pidfile;
char *node, *desc; /* node name & description */
@@ -128,6 +129,7 @@
int cookie_len; /* max length of cookie captures */
#ifdef USE_OPENSSL
int sslcachesize; /* SSL cache size in session, defaults to 20000 */
+ int sslprivatecache; /* Force to use a private session cache even if nbproc > 1 */
unsigned int ssllifetime; /* SSL session lifetime in seconds */
unsigned int ssl_max_record; /* SSL max record size */
#endif
@@ -147,7 +149,7 @@
} ux;
} unix_bind;
#ifdef USE_CPU_AFFINITY
- unsigned long cpu_map[32]; /* list of CPU masks for the 32 first processes */
+ unsigned long cpu_map[LONGBITS]; /* list of CPU masks for the 32/64 first processes */
#endif
struct proxy *stats_fe; /* the frontend holding the stats settings */
};
@@ -169,6 +171,24 @@
extern char localpeer[MAX_HOSTNAME_LEN];
extern struct list global_listener_queue; /* list of the temporarily limited listeners */
extern struct task *global_listener_queue_task;
+extern unsigned int warned; /* bitfield of a few warnings to emit just once */
+
+/* bit values to go with "warned" above */
+#define WARN_BLOCK_DEPRECATED 0x00000001
+#define WARN_REQSETBE_DEPRECATED 0x00000002
+#define WARN_REDISPATCH_DEPRECATED 0x00000004
+#define WARN_CLITO_DEPRECATED 0x00000008
+#define WARN_SRVTO_DEPRECATED 0x00000010
+#define WARN_CONTO_DEPRECATED 0x00000020
+
+/* to be used with warned and WARN_* */
+static inline int already_warned(unsigned int warning)
+{
+ if (warned & warning)
+ return 1;
+ warned |= warning;
+ return 0;
+}
#endif /* _TYPES_GLOBAL_H */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/listener.h
^
|
@@ -134,6 +134,7 @@
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
#endif
int is_ssl; /* SSL is required for these listeners */
+ unsigned long bind_proc; /* bitmask of processes allowed to use these listeners */
struct { /* UNIX socket permissions */
uid_t uid; /* -1 to leave unchanged */
gid_t gid; /* -1 to leave unchanged */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/pattern.h
^
|
@@ -61,11 +61,15 @@
PAT_MATCH = 3, /* sample matched at least one pattern */
};
-/* possible flags for expressions or patterns */
+/* possible flags for patterns matching or parsing */
enum {
- PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */
- PAT_F_TREE = 1 << 1, /* some patterns are arranged in a tree */
- PAT_F_NO_DNS = 1 << 2, /* dont perform any DNS requests */
+ PAT_MF_IGNORE_CASE = 1 << 0, /* ignore case */
+ PAT_MF_NO_DNS = 1 << 1, /* dont perform any DNS requests */
+};
+
+/* possible flags for patterns storage */
+enum {
+ PAT_SF_TREE = 1 << 0, /* some patterns are arranged in a tree */
};
/* ACL match methods */
@@ -163,7 +167,7 @@
struct my_regex *reg; /* a compiled regex */
} ptr; /* indirect values, allocated */
int len; /* data length when required */
- int flags; /* expr or pattern flags. */
+ int sflags; /* flags relative to the storage method. */
struct sample_storage *smp; /* used to store a pointer to sample value associated
with the match. It is used with maps */
struct pat_ref_elt *ref;
@@ -191,6 +195,7 @@
struct list patterns; /* list of acl_patterns */
struct eb_root pattern_tree; /* may be used for lookup in large datasets */
struct eb_root pattern_tree_2; /* may be used for different types */
+ int mflags; /* flags relative to the parsing or matching method. */
};
/* This is a list of expression. A struct pattern_expr can be used by
@@ -205,7 +210,7 @@
/* This struct contain a list of pattern expr */
struct pattern_head {
- int (*parse)(const char *text, struct pattern *pattern, char **err);
+ int (*parse)(const char *text, struct pattern *pattern, int flags, char **err);
int (*parse_smp)(const char *text, struct sample_storage *smp);
int (*index)(struct pattern_expr *, struct pattern *, char **);
void (*delete)(struct pattern_expr *, struct pat_ref_elt *);
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/proto_http.h
^
|
@@ -252,6 +252,12 @@
HTTP_REQ_ACT_SET_LOGL,
HTTP_REQ_ACT_SET_TOS,
HTTP_REQ_ACT_SET_MARK,
+ HTTP_REQ_ACT_ADD_ACL,
+ HTTP_REQ_ACT_DEL_ACL,
+ HTTP_REQ_ACT_DEL_MAP,
+ HTTP_REQ_ACT_SET_MAP,
+ HTTP_REQ_ACT_CUSTOM_STOP,
+ HTTP_REQ_ACT_CUSTOM_CONT,
HTTP_REQ_ACT_MAX /* must always be last */
};
@@ -267,9 +273,25 @@
HTTP_RES_ACT_SET_LOGL,
HTTP_RES_ACT_SET_TOS,
HTTP_RES_ACT_SET_MARK,
+ HTTP_RES_ACT_ADD_ACL,
+ HTTP_RES_ACT_DEL_ACL,
+ HTTP_RES_ACT_DEL_MAP,
+ HTTP_RES_ACT_SET_MAP,
+ HTTP_RES_ACT_CUSTOM_STOP, /* used for module keywords */
+ HTTP_RES_ACT_CUSTOM_CONT, /* used for module keywords */
HTTP_RES_ACT_MAX /* must always be last */
};
+/* final results for http-request rules */
+enum rule_result {
+ HTTP_RULE_RES_CONT = 0, /* nothing special, continue rules evaluation */
+ HTTP_RULE_RES_STOP, /* stopped processing on an accept */
+ HTTP_RULE_RES_DENY, /* deny (or tarpit if TX_CLTARPIT) */
+ HTTP_RULE_RES_ABRT, /* abort request, msg already sent (eg: auth) */
+ HTTP_RULE_RES_DONE, /* processing done, stop processing (eg: redirect) */
+ HTTP_RULE_RES_BADREQ, /* bad request */
+};
+
/*
* All implemented return codes
*/
@@ -386,10 +408,15 @@
char *user, *pass; /* extracted username & password */
};
+struct proxy;
+struct http_txn;
+struct session;
+
struct http_req_rule {
struct list list;
struct acl_cond *cond; /* acl condition to meet */
unsigned int action; /* HTTP_REQ_* */
+ int (*action_ptr)(struct http_req_rule *rule, struct proxy *px, struct session *s, struct http_txn *http_txn); /* ptr to custom action */
union {
struct {
char *realm;
@@ -404,6 +431,12 @@
int loglevel; /* log-level value for HTTP_REQ_ACT_SET_LOGL */
int tos; /* tos value for HTTP_REQ_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_REQ_ACT_SET_MARK */
+ void *data; /* generic pointer for module or external rule */
+ struct {
+ char *ref; /* MAP or ACL file name to update */
+ struct list key; /* pattern to retrieve MAP or ACL key */
+ struct list value; /* pattern to retrieve MAP value */
+ } map;
} arg; /* arguments used by some actions */
};
@@ -411,6 +444,7 @@
struct list list;
struct acl_cond *cond; /* acl condition to meet */
unsigned int action; /* HTTP_RES_* */
+ int (*action_ptr)(struct http_res_rule *rule, struct proxy *px, struct session *s, struct http_txn *http_txn); /* ptr to custom action */
union {
struct {
char *name; /* header name */
@@ -421,6 +455,12 @@
int loglevel; /* log-level value for HTTP_RES_ACT_SET_LOGL */
int tos; /* tos value for HTTP_RES_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_RES_ACT_SET_MARK */
+ void *data; /* generic pointer for module or external rule */
+ struct {
+ char *ref; /* MAP or ACL file name to update */
+ struct list key; /* pattern to retrieve MAP or ACL key */
+ struct list value; /* pattern to retrieve MAP value */
+ } map;
} arg; /* arguments used by some actions */
};
@@ -446,6 +486,7 @@
struct http_auth_data auth; /* HTTP auth data */
};
+
/* This structure is used by http_find_header() to return values of headers.
* The header starts at <line>, the value (excluding leading and trailing white
* spaces) at <line>+<val> for <vlen> bytes, followed by optional <tws> trailing
@@ -468,6 +509,31 @@
int len;
};
+struct http_req_action_kw {
+ const char *kw;
+ int (*parse)(const char **args, int *cur_arg, struct proxy *px, struct http_req_rule *rule, char **err);
+};
+
+struct http_res_action_kw {
+ const char *kw;
+ int (*parse)(const char **args, int *cur_arg, struct proxy *px, struct http_res_rule *rule, char **err);
+};
+
+struct http_req_action_kw_list {
+ const char *scope;
+ struct list list;
+ struct http_req_action_kw kw[VAR_ARRAY];
+};
+
+struct http_res_action_kw_list {
+ const char *scope;
+ struct list list;
+ struct http_res_action_kw kw[VAR_ARRAY];
+};
+
+extern struct http_req_action_kw_list http_req_keywords;
+extern struct http_res_action_kw_list http_res_keywords;
+
extern const struct http_method_name http_known_methods[HTTP_METH_OTHER];
#endif /* _TYPES_PROTO_HTTP_H */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/proxy.h
^
|
@@ -228,7 +228,7 @@
struct list acl; /* ACL declared on this proxy */
struct list http_req_rules; /* HTTP request rules: allow/deny/... */
struct list http_res_rules; /* HTTP response rules: allow/deny/... */
- struct list block_cond; /* early blocking conditions (chained) */
+ struct list block_rules; /* http-request block rules to be inserted before other ones */
struct list redirect_rules; /* content redirecting rules (chained) */
struct list switching_rules; /* content switching rules (chained) */
struct list persist_rules; /* 'force-persist' and 'ignore-persist' rules (chained) */
@@ -284,11 +284,14 @@
int httpka; /* maximum time for a new HTTP request when using keep-alive */
int check; /* maximum time for complete check */
int tunnel; /* I/O timeout to use in tunnel mode (in ticks) */
+ int clientfin; /* timeout to apply to client half-closed connections */
+ int serverfin; /* timeout to apply to server half-closed connections */
} timeout;
char *id, *desc; /* proxy id (name) and description */
struct list pendconns; /* pending connections with no server assigned yet */
int nbpend; /* number of pending connections with no server assigned yet */
int totpend; /* total number of pending connections on this instance (for stats) */
+ int max_ka_queue; /* 1+maximum requests in queue accepted for reusing a K-A conn (0=none) */
unsigned int feconn, beconn; /* # of active frontend and backends sessions */
struct freq_ctr fe_req_per_sec; /* HTTP requests per second on the frontend */
struct freq_ctr fe_conn_per_sec; /* received connections per second on the frontend */
@@ -344,7 +347,7 @@
struct chunk errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */
int uuid; /* universally unique proxy ID, used for SNMP */
unsigned int backlog; /* force the frontend's listen backlog */
- unsigned int bind_proc; /* bitmask of processes using this proxy. 0 = all. */
+ unsigned long bind_proc; /* bitmask of processes using this proxy */
/* warning: these structs are huge, keep them at the bottom */
struct sockaddr_storage dispatch_addr; /* the default address to connect to */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/include/types/server.h
^
|
@@ -53,10 +53,15 @@
#define SRV_WARMINGUP 0x0040 /* this server is warming up after a failure */
#define SRV_MAINTAIN 0x0080 /* this server is in maintenance mode */
#define SRV_DRAIN 0x0100 /* this server has been requested to drain its connections */
-/* unused: 0x0200, 0x0400 */
-#define SRV_SEND_PROXY 0x0800 /* this server talks the PROXY protocol */
+/* unused: 0x0200, 0x0400, 0x0800 */
#define SRV_NON_STICK 0x1000 /* never add connections allocated to this server to a stick table */
+/* configured server options for send-proxy (server->pp_opts) */
+#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
+#define SRV_PP_V2 0x0002 /* proxy protocol version 2 */
+#define SRV_PP_V2_SSL 0x0004 /* proxy protocol version 2 with SSL*/
+#define SRV_PP_V2_SSL_CN 0x0008 /* proxy protocol version 2 with SSL and CN*/
+
/* function which act on servers need to return various errors */
#define SRV_STATUS_OK 0 /* everything is OK. */
#define SRV_STATUS_INTERNAL 1 /* other unrecoverable errors. */
@@ -106,6 +111,7 @@
int rdr_len; /* the length of the redirection prefix */
char *cookie; /* the id set in the cookie */
char *rdr_pfx; /* the redirection prefix */
+ int pp_opts; /* proxy protocol options (SRV_PP_*) */
struct proxy *proxy; /* the proxy this server belongs to */
int served; /* # of active sessions currently being served (ie not pending) */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/acl.c
^
|
@@ -445,9 +445,9 @@
unique_id = -1;
while (**args == '-') {
if ((*args)[1] == 'i')
- patflags |= PAT_F_IGNORE_CASE;
+ patflags |= PAT_MF_IGNORE_CASE;
else if ((*args)[1] == 'n')
- patflags |= PAT_F_NO_DNS;
+ patflags |= PAT_MF_NO_DNS;
else if ((*args)[1] == 'u') {
unique_id = strtol(args[1], &error, 10);
if (*error != '\0') {
@@ -534,6 +534,9 @@
if (!pattern_expr)
goto out_free_expr;
+ /* Copy the pattern matching and indexing flags. */
+ pattern_expr->mflags = patflags;
+
/* now parse all patterns */
while (**args) {
arg = *args;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/auth.c
^
|
@@ -267,7 +267,7 @@
/* Check if the userlist is present in the context data. */
if (!ul)
- return PAT_NOMATCH;
+ return NULL;
/* Browse the userlist for searching user. */
for (u = ul->users; u; u = u->next) {
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/backend.c
^
|
@@ -541,8 +541,12 @@
if (conn &&
(conn->flags & CO_FL_CONNECTED) &&
- ((s->be->options & PR_O_PREF_LAST) || (s->txn.flags & TX_PREFER_LAST)) &&
objt_server(conn->target) && __objt_server(conn->target)->proxy == s->be &&
+ ((s->txn.flags & TX_PREFER_LAST) ||
+ ((s->be->options & PR_O_PREF_LAST) &&
+ (!s->be->max_ka_queue ||
+ server_has_room(__objt_server(conn->target)) ||
+ (__objt_server(conn->target)->nbpend + 1) < s->be->max_ka_queue))) &&
srv_is_usable(__objt_server(conn->target)->state, __objt_server(conn->target)->eweight)) {
/* This session was relying on a server in a previous request
* and the proxy has "option prefer-current-server" set, so
@@ -749,7 +753,8 @@
if (!is_addr(&srv_conn->addr.to) && cli_conn) {
/* if the server has no address, we use the same address
* the client asked, which is handy for remapping ports
- * locally on multiple addresses at once.
+ * locally on multiple addresses at once. Nothing is done
+ * for AF_UNIX addresses.
*/
conn_get_to_addr(cli_conn);
@@ -1055,7 +1060,7 @@
/* process the case where the server requires the PROXY protocol to be sent */
srv_conn->send_proxy_ofs = 0;
- if (objt_server(s->target) && (objt_server(s->target)->state & SRV_SEND_PROXY)) {
+ if (objt_server(s->target) && objt_server(s->target)->pp_opts) {
srv_conn->send_proxy_ofs = 1; /* must compute size */
cli_conn = objt_conn(s->req->prod->end);
if (cli_conn)
@@ -1110,7 +1115,7 @@
* that the connection is ready to use.
*/
-int srv_redispatch_connect(struct session *t)
+int srv_redispatch_connect(struct session *s)
{
struct server *srv;
int conn_err;
@@ -1119,8 +1124,8 @@
* try to get a new one, and wait in this state if it's queued
*/
redispatch:
- conn_err = assign_server_and_queue(t);
- srv = objt_server(t->target);
+ conn_err = assign_server_and_queue(s);
+ srv = objt_server(s->target);
switch (conn_err) {
case SRV_STATUS_OK:
@@ -1131,42 +1136,42 @@
* and we can redispatch to another server, or it is not and we return
* 503. This only makes sense in DIRECT mode however, because normal LB
* algorithms would never select such a server, and hash algorithms
- * would bring us on the same server again. Note that t->target is set
+ * would bring us on the same server again. Note that s->target is set
* in this case.
*/
- if (((t->flags & (SN_DIRECT|SN_FORCE_PRST)) == SN_DIRECT) &&
- (t->be->options & PR_O_REDISP)) {
- t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
+ if (((s->flags & (SN_DIRECT|SN_FORCE_PRST)) == SN_DIRECT) &&
+ (s->be->options & PR_O_REDISP)) {
+ s->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
goto redispatch;
}
- if (!t->req->cons->err_type) {
- t->req->cons->err_type = SI_ET_QUEUE_ERR;
+ if (!s->req->cons->err_type) {
+ s->req->cons->err_type = SI_ET_QUEUE_ERR;
}
srv->counters.failed_conns++;
- t->be->be_counters.failed_conns++;
+ s->be->be_counters.failed_conns++;
return 1;
case SRV_STATUS_NOSRV:
/* note: it is guaranteed that srv == NULL here */
- if (!t->req->cons->err_type) {
- t->req->cons->err_type = SI_ET_CONN_ERR;
+ if (!s->req->cons->err_type) {
+ s->req->cons->err_type = SI_ET_CONN_ERR;
}
- t->be->be_counters.failed_conns++;
+ s->be->be_counters.failed_conns++;
return 1;
case SRV_STATUS_QUEUED:
- t->req->cons->exp = tick_add_ifset(now_ms, t->be->timeout.queue);
- t->req->cons->state = SI_ST_QUE;
+ s->req->cons->exp = tick_add_ifset(now_ms, s->be->timeout.queue);
+ s->req->cons->state = SI_ST_QUE;
/* do nothing else and do not wake any other session up */
return 1;
case SRV_STATUS_INTERNAL:
default:
- if (!t->req->cons->err_type) {
- t->req->cons->err_type = SI_ET_CONN_OTHER;
+ if (!s->req->cons->err_type) {
+ s->req->cons->err_type = SI_ET_CONN_OTHER;
}
if (srv)
@@ -1175,10 +1180,10 @@
srv_set_sess_last(srv);
if (srv)
srv->counters.failed_conns++;
- t->be->be_counters.failed_conns++;
+ s->be->be_counters.failed_conns++;
/* release other sessions waiting for this server */
- if (may_dequeue_tasks(srv, t->be))
+ if (may_dequeue_tasks(srv, s->be))
process_srv_queue(srv);
return 1;
}
@@ -1235,7 +1240,8 @@
s->target = NULL;
while (srv) {
- if (memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) {
+ if (srv->addr.ss_family == AF_INET &&
+ memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) {
if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
/* we found the server and it is usable */
s->flags |= SN_DIRECT | SN_ASSIGNED;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/cfgparse.c
^
|
@@ -312,7 +312,7 @@
*/
int warnif_rule_after_block(struct proxy *proxy, const char *file, int line, const char *arg)
{
- if (!LIST_ISEMPTY(&proxy->block_cond)) {
+ if (!LIST_ISEMPTY(&proxy->block_rules)) {
Warning("parsing [%s:%d] : a '%s' rule placed after a 'block' rule will still be processed before.\n",
file, line, arg);
return 1;
@@ -594,6 +594,9 @@
global.tune.chksize = atol(args[1]);
}
#ifdef USE_OPENSSL
+ else if (!strcmp(args[0], "tune.ssl.force-private-cache")) {
+ global.tune.sslprivatecache = 1;
+ }
else if (!strcmp(args[0], "tune.ssl.cachesize")) {
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
@@ -883,6 +886,12 @@
goto out;
}
global.nbproc = atol(args[1]);
+ if (global.nbproc < 1 || global.nbproc > LONGBITS) {
+ Alert("parsing [%s:%d] : '%s' must be between 1 and %d (was %d).\n",
+ file, linenum, args[0], LONGBITS, global.nbproc);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
}
else if (!strcmp(args[0], "maxconn")) {
if (global.maxconn != 0) {
@@ -1341,27 +1350,49 @@
err_code |= ERR_ALERT | ERR_FATAL;
}
}
+ else if (!strcmp(args[0], "max-spread-checks")) { /* maximum time between first and last check */
+ const char *err;
+ unsigned int val;
+
+
+ if (*(args[1]) == 0) {
+ Alert("parsing [%s:%d]: '%s' expects an integer argument (0..50).\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ err = parse_time_err(args[1], &val, TIME_UNIT_MS);
+ if (err) {
+ Alert("parsing [%s:%d]: unsupported character '%c' in '%s' (wants an integer delay).\n", file, linenum, *err, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ }
+ global.max_spread_checks = val;
+ if (global.max_spread_checks < 0) {
+ Alert("parsing [%s:%d]: '%s' needs a positive delay in milliseconds.\n",file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ }
+ }
else if (strcmp(args[0], "cpu-map") == 0) { /* map a process list to a CPU set */
#ifdef USE_CPU_AFFINITY
int cur_arg, i;
- unsigned int proc = 0;
+ unsigned long proc = 0;
unsigned long cpus = 0;
if (strcmp(args[1], "all") == 0)
- proc = 0xFFFFFFFF;
+ proc = ~0UL;
else if (strcmp(args[1], "odd") == 0)
- proc = 0x55555555;
+ proc = ~0UL/3UL; /* 0x555....555 */
else if (strcmp(args[1], "even") == 0)
- proc = 0xAAAAAAAA;
+ proc = (~0UL/3UL) << 1; /* 0xAAA...AAA */
else {
- proc = atoi(args[1]);
- if (proc >= 1 && proc <= 32)
- proc = 1 << (proc - 1);
+ proc = atol(args[1]);
+ if (proc >= 1 && proc <= LONGBITS)
+ proc = 1UL << (proc - 1);
}
if (!proc || !*args[2]) {
- Alert("parsing [%s:%d]: %s expects a process number including 'all', 'odd', 'even', or a number from 1 to 32, followed by a list of CPU ranges with numbers from 0 to 31.\n",
- file, linenum, args[0]);
+ Alert("parsing [%s:%d]: %s expects a process number including 'all', 'odd', 'even', or a number from 1 to %d, followed by a list of CPU ranges with numbers from 0 to %d.\n",
+ file, linenum, args[0], LONGBITS, LONGBITS - 1);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@@ -1383,9 +1414,9 @@
high = swap;
}
- if (low < 0 || high >= sizeof(long) * 8) {
+ if (high >= LONGBITS) {
Alert("parsing [%s:%d]: %s supports CPU numbers from 0 to %d.\n",
- file, linenum, args[0], (int)(sizeof(long) * 8 - 1));
+ file, linenum, args[0], LONGBITS - 1);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@@ -1401,8 +1432,8 @@
}
cur_arg++;
}
- for (i = 0; i < 32; i++)
- if (proc & (1 << i))
+ for (i = 0; i < LONGBITS; i++)
+ if (proc & (1UL << i))
global.cpu_map[i] = cpus;
#else
Alert("parsing [%s:%d] : '%s' is not enabled, please check build options for USE_CPU_AFFINITY.\n", file, linenum, args[0]);
@@ -1726,6 +1757,7 @@
}
list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+ l->maxaccept = 1;
l->maxconn = ((struct proxy *)curpeers->peers_fe)->maxconn;
l->backlog = ((struct proxy *)curpeers->peers_fe)->backlog;
l->timeout = &((struct proxy *)curpeers->peers_fe)->timeout.client;
@@ -1908,6 +1940,7 @@
if (curproxy->cap & PR_CAP_BE) {
curproxy->fullconn = defproxy.fullconn;
curproxy->conn_retries = defproxy.conn_retries;
+ curproxy->max_ka_queue = defproxy.max_ka_queue;
if (defproxy.check_req) {
curproxy->check_req = calloc(1, defproxy.check_len);
@@ -1971,7 +2004,6 @@
curproxy->timeout.tarpit = defproxy.timeout.tarpit;
curproxy->timeout.httpreq = defproxy.timeout.httpreq;
curproxy->timeout.httpka = defproxy.timeout.httpka;
- curproxy->uri_auth = defproxy.uri_auth;
curproxy->mon_net = defproxy.mon_net;
curproxy->mon_mask = defproxy.mon_mask;
if (defproxy.monitor_uri)
@@ -2007,6 +2039,7 @@
}
curproxy->mode = defproxy.mode;
+ curproxy->uri_auth = defproxy.uri_auth; /* for stats */
/* copy default logsrvs to curproxy */
list_for_each_entry(tmplogsrv, &defproxy.logsrvs, list) {
@@ -2324,7 +2357,7 @@
}
else if (!strcmp(args[0], "bind-process")) { /* enable this proxy only on some processes */
int cur_arg = 1;
- unsigned int set = 0;
+ unsigned long set = 0;
while (*args[cur_arg]) {
unsigned int low, high;
@@ -2334,10 +2367,10 @@
break;
}
else if (strcmp(args[cur_arg], "odd") == 0) {
- set |= 0x55555555;
+ set |= ~0UL/3UL; /* 0x555....555 */
}
else if (strcmp(args[cur_arg], "even") == 0) {
- set |= 0xAAAAAAAA;
+ set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
}
else if (isdigit((int)*args[cur_arg])) {
char *dash = strchr(args[cur_arg], '-');
@@ -2352,24 +2385,18 @@
high = swap;
}
- if (low < 1 || high > 32) {
- Alert("parsing [%s:%d]: %s supports process numbers from 1 to 32.\n",
- file, linenum, args[0]);
+ if (low < 1 || high > LONGBITS) {
+ Alert("parsing [%s:%d]: %s supports process numbers from 1 to %d.\n",
+ file, linenum, args[0], LONGBITS);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
-
- if (high > global.nbproc) {
- Warning("parsing [%s:%d]: %s references process number %d which is higher than global.nbproc (%d).\n",
- file, linenum, args[0], high, global.nbproc);
- err_code |= ERR_WARN;
- }
while (low <= high)
- set |= 1 << (low++ - 1);
+ set |= 1UL << (low++ - 1);
}
else {
- Alert("parsing [%s:%d]: %s expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to 32.\n",
- file, linenum, args[0]);
+ Alert("parsing [%s:%d]: %s expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
+ file, linenum, args[0], LONGBITS);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@@ -2851,28 +2878,32 @@
curproxy->server_id_hdr_len = strlen(curproxy->server_id_hdr_name);
}
else if (!strcmp(args[0], "block")) { /* early blocking based on ACLs */
+ struct http_req_rule *rule;
+
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
- if (strcmp(args[1], "if") != 0 && strcmp(args[1], "unless") != 0) {
- Alert("parsing [%s:%d] : '%s' requires either 'if' or 'unless' followed by a condition.\n",
- file, linenum, args[0]);
- err_code |= ERR_ALERT | ERR_FATAL;
+ /* emulate "block" using "http-request block". Since these rules are supposed to
+ * be processed before all http-request rules, we put them into their own list
+ * and will insert them at the end.
+ */
+ rule = parse_http_req_cond((const char **)args, file, linenum, curproxy);
+ if (!rule) {
+ err_code |= ERR_ALERT | ERR_ABORT;
goto out;
}
+ err_code |= warnif_misplaced_block(curproxy, file, linenum, args[0]);
+ err_code |= warnif_cond_conflicts(rule->cond,
+ (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+ LIST_ADDQ(&curproxy->block_rules, &rule->list);
- if ((cond = build_acl_cond(file, linenum, curproxy, (const char **)args + 1, &errmsg)) == NULL) {
- Alert("parsing [%s:%d] : error detected while parsing blocking condition : %s.\n",
- file, linenum, errmsg);
- err_code |= ERR_ALERT | ERR_FATAL;
- goto out;
- }
+ if (!already_warned(WARN_BLOCK_DEPRECATED))
+ Warning("parsing [%s:%d] : The '%s' directive is now deprecated in favor of 'http-request deny' which uses the exact same syntax. The rules are translated but support might disappear in a future version.\n", file, linenum, args[0]);
- LIST_ADDQ(&curproxy->block_cond, &cond->list);
- warnif_misplaced_block(curproxy, file, linenum, args[0]);
}
else if (!strcmp(args[0], "redirect")) {
struct redirect_rule *rule;
@@ -3284,9 +3315,6 @@
LIST_ADDQ(&curproxy->sticking_rules, &rule->list);
}
else if (!strcmp(args[0], "stats")) {
- if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
- err_code |= ERR_WARN;
-
if (curproxy != &defproxy && curproxy->uri_auth == defproxy.uri_auth)
curproxy->uri_auth = NULL; /* we must detach from the default config */
@@ -4038,7 +4066,8 @@
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
err_code |= ERR_WARN;
- Warning("parsing [%s:%d]: keyword '%s' is deprecated, please use 'option redispatch' instead.\n",
+ if (!already_warned(WARN_REDISPATCH_DEPRECATED))
+ Warning("parsing [%s:%d]: keyword '%s' is deprecated in favor of 'option redispatch', and will not be supported by future versions.\n",
file, linenum, args[0]);
err_code |= ERR_WARN;
/* enable reconnections to dispatch */
@@ -4784,7 +4813,7 @@
if (!strcmp(args[cur_arg], "usesrc")) { /* address to use outside */
#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
#if !defined(CONFIG_HAP_TRANSPARENT)
- if (!is_addr(&curproxy->conn_src.source_addr)) {
+ if (!is_inet_addr(&curproxy->conn_src.source_addr)) {
Alert("parsing [%s:%d] : '%s' requires an explicit 'source' address.\n",
file, linenum, "usesrc");
err_code |= ERR_ALERT | ERR_FATAL;
@@ -4973,6 +5002,9 @@
args[0], args[1], args[2], (const char **)args+3);
if (err_code & ERR_FATAL)
goto out;
+
+ if (!already_warned(WARN_REQSETBE_DEPRECATED))
+ Warning("parsing [%s:%d] : The '%s' directive is now deprecated in favor of the more efficient 'use_backend' which uses a different but more powerful syntax. Future versions will not support '%s' anymore, you should convert it now!\n", file, linenum, args[0], args[0]);
}
else if (!strcmp(args[0], "reqisetbe")) { /* switch the backend from a regex, ignoring case */
err_code |= create_cond_regex_rule(file, linenum, curproxy,
@@ -4980,6 +5012,9 @@
args[0], args[1], args[2], (const char **)args+3);
if (err_code & ERR_FATAL)
goto out;
+
+ if (!already_warned(WARN_REQSETBE_DEPRECATED))
+ Warning("parsing [%s:%d] : The '%s' directive is now deprecated in favor of the more efficient 'use_backend' which uses a different but more powerful syntax. Future versions will not support '%s' anymore, you should convert it now!\n", file, linenum, args[0], args[0]);
}
else if (!strcmp(args[0], "reqirep")) { /* replace request header from a regex, ignoring case */
if (*(args[2]) == 0) {
@@ -5817,8 +5852,65 @@
continue;
}
- /* number of processes this proxy is bound to */
- nbproc = curproxy->bind_proc ? popcount(curproxy->bind_proc) : global.nbproc;
+ /* Check multi-process mode compatibility for the current proxy */
+
+ if (curproxy->bind_proc) {
+ /* an explicit bind-process was specified, let's check how many
+ * processes remain.
+ */
+ nbproc = popcount(curproxy->bind_proc);
+
+ curproxy->bind_proc &= nbits(global.nbproc);
+ if (!curproxy->bind_proc && nbproc == 1) {
+ Warning("Proxy '%s': the process specified on the 'bind-process' directive refers to a process number that is higher than global.nbproc. The proxy has been forced to run on process 1 only.\n", curproxy->id);
+ curproxy->bind_proc = 1;
+ }
+ else if (!curproxy->bind_proc && nbproc > 1) {
+ Warning("Proxy '%s': all processes specified on the 'bind-process' directive refer to numbers that are all higher than global.nbproc. The directive was ignored and the proxy will run on all processes.\n", curproxy->id);
+ curproxy->bind_proc = 0;
+ }
+ }
+
+ /* check and reduce the bind-proc of each listener */
+ list_for_each_entry(bind_conf, &curproxy->conf.bind, by_fe) {
+ unsigned long mask;
+
+ if (!bind_conf->bind_proc)
+ continue;
+
+ mask = nbits(global.nbproc);
+ if (curproxy->bind_proc)
+ mask &= curproxy->bind_proc;
+ /* mask cannot be null here thanks to the previous checks */
+
+ nbproc = popcount(bind_conf->bind_proc);
+ bind_conf->bind_proc &= mask;
+
+ if (!bind_conf->bind_proc && nbproc == 1) {
+ Warning("Proxy '%s': the process number specified on the 'process' directive of 'bind %s' at [%s:%d] refers to a process not covered by the proxy. This has been fixed by forcing it to run on the proxy's first process only.\n",
+ curproxy->id, bind_conf->arg, bind_conf->file, bind_conf->line);
+ bind_conf->bind_proc = mask & ~(mask - 1);
+ }
+ else if (!bind_conf->bind_proc && nbproc > 1) {
+ Warning("Proxy '%s': the process range specified on the 'process' directive of 'bind %s' at [%s:%d] only refers to processes not covered by the proxy. The directive was ignored so that all of the proxy's processes are used.\n",
+ curproxy->id, bind_conf->arg, bind_conf->file, bind_conf->line);
+ bind_conf->bind_proc = 0;
+ }
+ }
+
+ /* here, if bind_proc is null, it means no limit, otherwise it's explicit.
+ * We now check how many processes the proxy will effectively run on.
+ */
+
+ nbproc = global.nbproc;
+ if (curproxy->bind_proc)
+ nbproc = popcount(curproxy->bind_proc & nbits(global.nbproc));
+
+ if (global.nbproc > 1 && curproxy->table.peers.name) {
+ Alert("Proxy '%s': peers can't be used in multi-process mode (nbproc > 1).\n",
+ curproxy->id);
+ cfgerr++;
+ }
switch (curproxy->mode) {
case PR_MODE_HEALTH:
@@ -5914,8 +6006,9 @@
/* we force the backend to be present on at least all of
* the frontend's processes.
*/
- target->bind_proc = curproxy->bind_proc ?
- (target->bind_proc | curproxy->bind_proc) : 0;
+ if (target->bind_proc)
+ target->bind_proc = curproxy->bind_proc ?
+ (target->bind_proc | curproxy->bind_proc) : 0;
/* Emit a warning if this proxy also has some servers */
if (curproxy->srv) {
@@ -5951,8 +6044,9 @@
/* we force the backend to be present on at least all of
* the frontend's processes.
*/
- target->bind_proc = curproxy->bind_proc ?
- (target->bind_proc | curproxy->bind_proc) : 0;
+ if (target->bind_proc)
+ target->bind_proc = curproxy->bind_proc ?
+ (target->bind_proc | curproxy->bind_proc) : 0;
}
}
}
@@ -6004,8 +6098,9 @@
/* we force the backend to be present on at least all of
* the frontend's processes.
*/
- target->bind_proc = curproxy->bind_proc ?
- (target->bind_proc | curproxy->bind_proc) : 0;
+ if (target->bind_proc)
+ target->bind_proc = curproxy->bind_proc ?
+ (target->bind_proc | curproxy->bind_proc) : 0;
}
}
@@ -6169,6 +6264,16 @@
}
}
+ /* move any "block" rules at the beginning of the http-request rules */
+ if (!LIST_ISEMPTY(&curproxy->block_rules)) {
+ /* insert block_rules into http_req_rules at the beginning */
+ curproxy->block_rules.p->n = curproxy->http_req_rules.n;
+ curproxy->http_req_rules.n->p = curproxy->block_rules.p;
+ curproxy->block_rules.n->p = &curproxy->http_req_rules;
+ curproxy->http_req_rules.n = curproxy->block_rules.n;
+ LIST_INIT(&curproxy->block_rules);
+ }
+
if (curproxy->table.peers.name) {
struct peers *curpeers = peers;
@@ -6703,6 +6808,8 @@
* remains NULL so that listeners can later detach.
*/
list_for_each_entry(bind_conf, &curproxy->conf.bind, by_fe) {
+ int alloc_ctx;
+
if (!bind_conf->is_ssl) {
if (bind_conf->default_ctx) {
Warning("Proxy '%s': A certificate was specified but SSL was not enabled on bind '%s' at [%s:%d] (use 'ssl').\n",
@@ -6717,8 +6824,12 @@
continue;
}
- if (shared_context_init(global.tune.sslcachesize, (global.nbproc > 1) ? 1 : 0) < 0) {
- Alert("Unable to allocate SSL session cache.\n");
+ alloc_ctx = shared_context_init(global.tune.sslcachesize, (!global.tune.sslprivatecache && (global.nbproc > 1)) ? 1 : 0);
+ if (alloc_ctx < 0) {
+ if (alloc_ctx == SHCTX_E_INIT_LOCK)
+ Alert("Unable to initialize the lock for the shared SSL session cache. You can retry using the global statement 'tune.ssl.force-private-cache' but it could increase CPU usage due to renegotiations if nbproc > 1.\n");
+ else
+ Alert("Unable to allocate SSL session cache.\n");
cfgerr++;
continue;
}
@@ -6801,41 +6912,22 @@
#endif /* USE_OPENSSL */
}
- /* Check multi-process mode compatibility for the current proxy */
- if (global.nbproc > 1) {
- int nbproc = 0;
- if (curproxy->bind_proc) {
- int proc;
- for (proc = 0; proc < global.nbproc; proc++) {
- if (curproxy->bind_proc & (1 << proc)) {
- nbproc++;
- }
+ if (nbproc > 1) {
+ if (curproxy->uri_auth) {
+ Warning("Proxy '%s': in multi-process mode, stats will be limited to process assigned to the current request.\n",
+ curproxy->id);
+ if (!LIST_ISEMPTY(&curproxy->uri_auth->admin_rules)) {
+ Warning("Proxy '%s': stats admin will not work correctly in multi-process mode.\n",
+ curproxy->id);
}
- } else {
- nbproc = global.nbproc;
}
- if (curproxy->table.peers.name) {
- Alert("Proxy '%s': peers can't be used in multi-process mode (nbproc > 1).\n",
- curproxy->id);
- cfgerr++;
- }
- if (nbproc > 1) {
- if (curproxy->uri_auth) {
- Warning("Proxy '%s': in multi-process mode, stats will be limited to process assigned to the current request.\n",
- curproxy->id);
- if (!LIST_ISEMPTY(&curproxy->uri_auth->admin_rules)) {
- Warning("Proxy '%s': stats admin will not work correctly in multi-process mode.\n",
- curproxy->id);
- }
- }
- if (curproxy->appsession_name) {
- Warning("Proxy '%s': appsession will not work correctly in multi-process mode.\n",
- curproxy->id);
- }
- if (!LIST_ISEMPTY(&curproxy->sticking_rules)) {
- Warning("Proxy '%s': sticking rules will not work correctly in multi-process mode.\n",
- curproxy->id);
- }
+ if (curproxy->appsession_name) {
+ Warning("Proxy '%s': appsession will not work correctly in multi-process mode.\n",
+ curproxy->id);
+ }
+ if (!LIST_ISEMPTY(&curproxy->sticking_rules)) {
+ Warning("Proxy '%s': sticking rules will not work correctly in multi-process mode.\n",
+ curproxy->id);
}
}
@@ -6859,8 +6951,22 @@
/* Check multi-process mode compatibility */
if (global.nbproc > 1) {
- if (global.stats_fe && !global.stats_fe->bind_proc) {
- Warning("stats socket will not work as expected in multi-process mode (nbproc > 1), you should force process binding using 'stats bind-process'.\n");
+ list_for_each_entry(bind_conf, &global.stats_fe->conf.bind, by_fe) {
+ unsigned long mask;
+
+ mask = nbits(global.nbproc);
+ if (global.stats_fe->bind_proc)
+ mask &= global.stats_fe->bind_proc;
+
+ if (bind_conf->bind_proc)
+ mask &= bind_conf->bind_proc;
+
+ /* stop here if more than one process is used */
+ if (popcount(mask) > 1)
+ break;
+ }
+ if (&bind_conf->by_fe != &global.stats_fe->conf.bind) {
+ Warning("stats socket will not work as expected in multi-process mode (nbproc > 1), you should force process binding globally using 'stats bind-process' or per socket using the 'process' attribute.\n");
}
}
@@ -6971,7 +7077,6 @@
while (*last) {
curpeers = *last;
if (curpeers->peers_fe) {
- LIST_NEXT(&curpeers->peers_fe->conf.listeners, struct listener *, by_fe)->maxaccept = 1;
last = &curpeers->next;
continue;
}
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/channel.c
^
|
@@ -174,7 +174,7 @@
return 0;
/* OK so the data fits in the buffer in one or two blocks */
- max = buffer_contig_space_with_res(chn->buf, chn->buf->size - max);
+ max = buffer_contig_space(chn->buf);
memcpy(bi_end(chn->buf), blk, MIN(len, max));
if (len > max)
memcpy(chn->buf->data, blk + max, len - max);
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/checks.c
^
|
@@ -1509,6 +1509,7 @@
struct check *check = t->context;
struct server *s = check->server;
struct connection *conn = check->conn;
+ struct protocol *proto;
int rv;
int ret;
int expired = tick_is_expired(t->expire, now_ms);
@@ -1573,12 +1574,16 @@
/* no client address */
clear_addr(&conn->addr.from);
- if (is_addr(&s->check_common.addr))
+ if (is_addr(&s->check_common.addr)) {
/* we'll connect to the check addr specified on the server */
conn->addr.to = s->check_common.addr;
- else
+ proto = s->check_common.proto;
+ }
+ else {
/* we'll connect to the addr on the server */
conn->addr.to = s->addr;
+ proto = s->proto;
+ }
if (check->port) {
set_host_port(&conn->addr.to, check->port);
@@ -1606,8 +1611,8 @@
* connect() when a pure TCP check is used (without PROXY protocol).
*/
ret = SN_ERR_INTERNAL;
- if (s->check_common.proto->connect)
- ret = s->check_common.proto->connect(conn, check->type, (check->type) ? 0 : 2);
+ if (proto->connect)
+ ret = proto->connect(conn, check->type, (check->type) ? 0 : 2);
conn->flags |= CO_FL_WAKE_DATA;
if (s->check.send_proxy) {
conn->send_proxy_ofs = 1;
@@ -1744,11 +1749,14 @@
t->process = process_chk;
t->context = check;
+ if (mininter < srv_getinter(check))
+ mininter = srv_getinter(check);
+
+ if (global.max_spread_checks && mininter > global.max_spread_checks)
+ mininter = global.max_spread_checks;
+
/* check this every ms */
- t->expire = tick_add(now_ms,
- MS_TO_TICKS(((mininter &&
- mininter >= srv_getinter(check)) ?
- mininter : srv_getinter(check)) * srvpos / nbcheck));
+ t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
check->start = now;
task_queue(t);
@@ -2072,15 +2080,16 @@
/* no client address */
clear_addr(&conn->addr.from);
- if (is_addr(&s->check_common.addr))
+ if (is_addr(&s->check_common.addr)) {
/* we'll connect to the check addr specified on the server */
conn->addr.to = s->check_common.addr;
- else
+ proto = s->check_common.proto;
+ }
+ else {
/* we'll connect to the addr on the server */
conn->addr.to = s->addr;
-
- /* protocol */
- proto = protocol_by_family(conn->addr.to.ss_family);
+ proto = s->proto;
+ }
/* port */
if (check->current_step->port)
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/chunk.c
^
|
@@ -207,8 +207,10 @@
int diff = 0;
do {
- if (--len < 0)
+ if (--len < 0) {
+ diff = (unsigned char)0 - (unsigned char)*str;
break;
+ }
diff = (unsigned char)*(s1++) - (unsigned char)*(str++);
} while (!diff);
return diff;
@@ -225,8 +227,10 @@
int diff = 0;
do {
- if (--len < 0)
+ if (--len < 0) {
+ diff = (unsigned char)0 - (unsigned char)*str;
break;
+ }
diff = (unsigned char)*s1 - (unsigned char)*str;
if (unlikely(diff)) {
unsigned int l = (unsigned char)*s1;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/compression.c
^
|
@@ -211,7 +211,7 @@
*/
int http_compression_buffer_end(struct session *s, struct buffer **in, struct buffer **out, int end)
{
- int to_forward, forwarded;
+ int to_forward;
int left;
struct http_msg *msg = &s->txn.rsp;
struct buffer *ib = *in, *ob = *out;
@@ -266,14 +266,13 @@
to_forward = ob->i;
/* update input rate */
- forwarded = ib->o - ob->o;
if (s->comp_ctx && s->comp_ctx->cur_lvl > 0) {
- update_freq_ctr(&global.comp_bps_in, forwarded);
- s->fe->fe_counters.comp_in += forwarded;
- s->be->be_counters.comp_in += forwarded;
+ update_freq_ctr(&global.comp_bps_in, msg->next);
+ s->fe->fe_counters.comp_in += msg->next;
+ s->be->be_counters.comp_in += msg->next;
} else {
- s->fe->fe_counters.comp_byp += forwarded;
- s->be->be_counters.comp_byp += forwarded;
+ s->fe->fe_counters.comp_byp += msg->next;
+ s->be->be_counters.comp_byp += msg->next;
}
/* copy the remaining data in the tmp buffer. */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/connection.c
^
|
@@ -437,6 +437,23 @@
return 0;
}
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote)
+{
+ int ret = 0;
+
+ if (srv && (srv->pp_opts & SRV_PP_V2)) {
+ ret = make_proxy_line_v2(buf, buf_len, srv, remote);
+ }
+ else {
+ if (remote)
+ ret = make_proxy_line_v1(buf, buf_len, &remote->addr.from, &remote->addr.to);
+ else
+ ret = make_proxy_line_v1(buf, buf_len, NULL, NULL);
+ }
+
+ return ret;
+}
+
/* Makes a PROXY protocol line from the two addresses. The output is sent to
* buffer <buf> for a maximum size of <buf_len> (including the trailing zero).
* It returns the number of bytes composing this line (including the trailing
@@ -444,7 +461,7 @@
* TCP6 and "UNKNOWN" formats. If any of <src> or <dst> is null, UNKNOWN is
* emitted as well.
*/
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst)
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst)
{
int ret = 0;
@@ -516,3 +533,113 @@
}
return ret;
}
+
+#ifdef USE_OPENSSL
+static int make_tlv(char *dest, int dest_len, char type, uint16_t length, char *value)
+{
+ struct tlv *tlv;
+
+ if (!dest || (length + sizeof(*tlv) > dest_len))
+ return 0;
+
+ tlv = (struct tlv *)dest;
+
+ tlv->type = type;
+ tlv->length_hi = length >> 8;
+ tlv->length_lo = length & 0x00ff;
+ memcpy(tlv->value, value, length);
+ return length + sizeof(*tlv);
+}
+#endif
+
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote)
+{
+ const char pp2_signature[12] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
+ int ret = 0;
+ struct proxy_hdr_v2 *hdr_p = (struct proxy_hdr_v2 *)buf;
+ union proxy_addr *addr_p = (union proxy_addr *)(buf + PP2_HEADER_LEN);
+ struct sockaddr_storage null_addr = {0};
+ struct sockaddr_storage *src = &null_addr;
+ struct sockaddr_storage *dst = &null_addr;
+#ifdef USE_OPENSSL
+ int tlv_len = 0;
+ char *value = NULL;
+ struct tlv_ssl *tlv;
+ int ssl_tlv_len = 0;
+#endif
+
+ if (buf_len < PP2_HEADER_LEN)
+ return 0;
+ memcpy(hdr_p->sig, pp2_signature, PP2_SIGNATURE_LEN);
+
+ if (remote) {
+ src = &remote->addr.from;
+ dst = &remote->addr.to;
+ }
+ if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
+ if (buf_len < PP2_HDR_LEN_INET)
+ return 0;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+ hdr_p->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+ addr_p->ipv4_addr.src_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr;
+ addr_p->ipv4_addr.dst_addr = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+ addr_p->ipv4_addr.src_port = ((struct sockaddr_in *)src)->sin_port;
+ addr_p->ipv4_addr.dst_port = ((struct sockaddr_in *)dst)->sin_port;
+ ret = PP2_HDR_LEN_INET;
+ }
+ else if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET6) {
+ if (buf_len < PP2_HDR_LEN_INET6)
+ return 0;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+ hdr_p->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+ memcpy(addr_p->ipv6_addr.src_addr, &((struct sockaddr_in6 *)src)->sin6_addr, 16);
+ memcpy(addr_p->ipv6_addr.dst_addr, &((struct sockaddr_in6 *)dst)->sin6_addr, 16);
+ addr_p->ipv6_addr.src_port = ((struct sockaddr_in6 *)src)->sin6_port;
+ addr_p->ipv6_addr.dst_port = ((struct sockaddr_in6 *)dst)->sin6_port;
+ ret = PP2_HDR_LEN_INET6;
+ }
+ else {
+ if (buf_len < PP2_HDR_LEN_UNSPEC)
+ return 0;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_LOCAL;
+ hdr_p->fam = PP2_FAM_UNSPEC | PP2_TRANS_UNSPEC;
+ ret = PP2_HDR_LEN_UNSPEC;
+ }
+
+#ifdef USE_OPENSSL
+ if (srv->pp_opts & SRV_PP_V2_SSL) {
+ if ((buf_len - ret) < sizeof(struct tlv_ssl))
+ return 0;
+ tlv = (struct tlv_ssl *)&buf[ret];
+ memset(tlv, 0, sizeof(struct tlv_ssl));
+ ssl_tlv_len += sizeof(struct tlv_ssl);
+ tlv->tlv.type = PP2_TYPE_SSL;
+ if (ssl_sock_is_ssl(remote)) {
+ tlv->client |= PP2_CLIENT_SSL;
+ value = ssl_sock_get_version(remote);
+ if (value) {
+ tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len-ret-ssl_tlv_len), PP2_TYPE_SSL_VERSION, strlen(value), value);
+ ssl_tlv_len += tlv_len;
+ }
+ if (ssl_sock_get_cert_used(remote)) {
+ tlv->client |= PP2_CLIENT_CERT;
+ tlv->verify = htonl(ssl_sock_get_verify_result(remote));
+ }
+ if (srv->pp_opts & SRV_PP_V2_SSL_CN) {
+ value = ssl_sock_get_common_name(remote);
+ if (value) {
+ tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len - ret - ssl_tlv_len), PP2_TYPE_SSL_CN, strlen(value), value);
+ ssl_tlv_len += tlv_len;
+ }
+ }
+ }
+ tlv->tlv.length_hi = (uint16_t)(ssl_tlv_len - sizeof(struct tlv)) >> 8;
+ tlv->tlv.length_lo = (uint16_t)(ssl_tlv_len - sizeof(struct tlv)) & 0x00ff;
+ ret += ssl_tlv_len;
+ }
+#endif
+
+ hdr_p->len = htons((uint16_t)(ret - PP2_HEADER_LEN));
+
+ return ret;
+}
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/dumpstats.c
^
|
@@ -372,7 +372,7 @@
}
else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
int cur_arg = 2;
- unsigned int set = 0;
+ unsigned long set = 0;
if (!global.stats_fe) {
if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
@@ -389,10 +389,10 @@
break;
}
else if (strcmp(args[cur_arg], "odd") == 0) {
- set |= 0x55555555;
+ set |= ~0UL/3UL; /* 0x555....555 */
}
else if (strcmp(args[cur_arg], "even") == 0) {
- set |= 0xAAAAAAAA;
+ set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
}
else if (isdigit((int)*args[cur_arg])) {
char *dash = strchr(args[cur_arg], '-');
@@ -407,19 +407,18 @@
high = swap;
}
- if (low < 1 || high > 32) {
- memprintf(err, "'%s %s' supports process numbers from 1 to 32.\n",
- args[0], args[1]);
+ if (low < 1 || high > LONGBITS) {
+ memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
+ args[0], args[1], LONGBITS);
return -1;
}
-
while (low <= high)
- set |= 1 << (low++ - 1);
+ set |= 1UL << (low++ - 1);
}
else {
memprintf(err,
- "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to 32.\n",
- args[0], args[1]);
+ "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
+ args[0], args[1], LONGBITS);
return -1;
}
cur_arg++;
@@ -4357,7 +4356,7 @@
struct appctx *appctx = objt_appctx(si->end);
chunk_printf(&trash,
- "HTTP/1.0 200 OK\r\n"
+ "HTTP/1.1 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n",
@@ -4367,7 +4366,12 @@
chunk_appendf(&trash, "Refresh: %d\r\n",
uri->refresh);
- chunk_appendf(&trash, "\r\n");
+ /* we don't send the CRLF in chunked mode, it will be sent with the first chunk's size */
+
+ if (appctx->ctx.stats.flags & STAT_CHUNKED)
+ chunk_appendf(&trash, "Transfer-Encoding: chunked\r\n");
+ else
+ chunk_appendf(&trash, "\r\n");
s->txn.status = 200;
s->logs.tv_request = now;
@@ -4453,8 +4457,53 @@
}
if (appctx->st0 == STAT_HTTP_DUMP) {
+ unsigned int prev_len = si->ib->buf->i;
+ unsigned int data_len;
+ unsigned int last_len;
+ unsigned int last_fwd = 0;
+
+ if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+ /* One difficulty we're facing is that we must prevent
+ * the input data from being automatically forwarded to
+ * the output area. For this, we temporarily disable
+ * forwarding on the channel.
+ */
+ last_fwd = si->ib->to_forward;
+ si->ib->to_forward = 0;
+ chunk_printf(&trash, "\r\n000000\r\n");
+ if (bi_putchk(si->ib, &trash) == -1) {
+ si->ib->to_forward = last_fwd;
+ goto fail;
+ }
+ }
+
+ data_len = si->ib->buf->i;
if (stats_dump_stat_to_buffer(si, s->be->uri_auth))
appctx->st0 = STAT_HTTP_DONE;
+
+ last_len = si->ib->buf->i;
+
+ /* Now we must either adjust or remove the chunk size. This is
+ * not easy because the chunk size might wrap at the end of the
+ * buffer, so we pretend we have nothing in the buffer, we write
+ * the size, then restore the buffer's contents. Note that we can
+ * only do that because no forwarding is scheduled on the stats
+ * applet.
+ */
+ if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+ si->ib->total -= (last_len - prev_len);
+ si->ib->buf->i -= (last_len - prev_len);
+
+ if (last_len != data_len) {
+ chunk_printf(&trash, "\r\n%06x\r\n", (last_len - data_len));
+ bi_putchk(si->ib, &trash);
+
+ si->ib->total += (last_len - data_len);
+ si->ib->buf->i += (last_len - data_len);
+ }
+ /* now re-enable forwarding */
+ channel_forward(si->ib, last_fwd);
+ }
}
if (appctx->st0 == STAT_HTTP_POST) {
@@ -4469,8 +4518,17 @@
appctx->st0 = STAT_HTTP_DONE;
}
- if (appctx->st0 == STAT_HTTP_DONE)
- si_shutw(si);
+ if (appctx->st0 == STAT_HTTP_DONE) {
+ if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+ chunk_printf(&trash, "\r\n0\r\n\r\n");
+ if (bi_putchk(si->ib, &trash) == -1)
+ goto fail;
+ }
+ /* eat the whole request */
+ bo_skip(si->ob, si->ob->buf->o);
+ res->flags |= CF_READ_NULL;
+ si_shutr(si);
+ }
if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
si_shutw(si);
@@ -4482,6 +4540,7 @@
}
}
+ fail:
/* update all other flags and resync with the other side */
si_update(si);
@@ -4952,6 +5011,12 @@
else
chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
+ /* case sensitive */
+ if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
+ chunk_appendf(&trash, ", case=insensitive");
+ else
+ chunk_appendf(&trash, ", case=sensitive");
+
/* Display no match, and set default value */
if (!pat) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
@@ -4969,17 +5034,11 @@
chunk_appendf(&trash, ", match=yes");
/* display index mode */
- if (pat->flags & PAT_F_TREE)
+ if (pat->sflags & PAT_SF_TREE)
chunk_appendf(&trash, ", idx=tree");
else
chunk_appendf(&trash, ", idx=list");
- /* case sensitive */
- if (pat->flags & PAT_F_IGNORE_CASE)
- chunk_appendf(&trash, ", case=insensitive");
- else
- chunk_appendf(&trash, ", case=sensitive");
-
/* display pattern */
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
if (pat->ref && pat->ref->pattern)
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/haproxy.c
^
|
@@ -205,6 +205,9 @@
struct task *global_listener_queue_task;
static struct task *manage_global_listener_queue(struct task *t);
+/* bitfield of a few warnings to emit just once (WARN_*) */
+unsigned int warned = 0;
+
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
@@ -1016,7 +1019,7 @@
free(cwl);
}
- list_for_each_entry_safe(cond, condb, &p->block_cond, list) {
+ list_for_each_entry_safe(cond, condb, &p->block_rules, list) {
LIST_DEL(&cond->list);
prune_acl_cond(cond);
free(cond);
@@ -1593,7 +1596,7 @@
px = proxy;
while (px != NULL) {
if (px->bind_proc && px->state != PR_STSTOPPED) {
- if (!(px->bind_proc & (1 << proc)))
+ if (!(px->bind_proc & (1UL << proc)))
stop_proxy(px);
}
px = px->next;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/listener.c
^
|
@@ -11,6 +11,7 @@
*/
#define _GNU_SOURCE
+#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
@@ -41,15 +42,27 @@
/* This function adds the specified listener's file descriptor to the polling
* lists if it is in the LI_LISTEN state. The listener enters LI_READY or
- * LI_FULL state depending on its number of connections.
+ * LI_FULL state depending on its number of connections. In deamon mode, we
+ * also support binding only the relevant processes to their respective
+ * listeners. We don't do that in debug mode however.
*/
void enable_listener(struct listener *listener)
{
if (listener->state == LI_LISTEN) {
- if (listener->nbconn < listener->maxconn) {
+ if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
+ listener->bind_conf->bind_proc &&
+ !(listener->bind_conf->bind_proc & (1UL << (relative_pid - 1)))) {
+ /* we don't want to enable this listener and don't
+ * want any fd event to reach it.
+ */
+ fd_stop_recv(listener->fd);
+ listener->state = LI_PAUSED;
+ }
+ else if (listener->nbconn < listener->maxconn) {
fd_want_recv(listener->fd);
listener->state = LI_READY;
- } else {
+ }
+ else {
listener->state = LI_FULL;
}
}
@@ -105,12 +118,19 @@
* limited and disabled listeners are handled, which means that this function
* may replace enable_listener(). The resulting state will either be LI_READY
* or LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
+ * Listeners bound to a different process are not woken up unless we're in
+ * foreground mode.
*/
int resume_listener(struct listener *l)
{
if (l->state < LI_PAUSED)
return 0;
+ if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
+ l->bind_conf->bind_proc &&
+ !(l->bind_conf->bind_proc & (1UL << (relative_pid - 1))))
+ return 0;
+
if (l->proto->sock_prot == IPPROTO_TCP &&
l->state == LI_PAUSED &&
listen(l->fd, l->backlog ? l->backlog : l->maxconn) != 0)
@@ -257,6 +277,7 @@
struct listener *l = fdtab[fd].owner;
struct proxy *p = l->frontend;
int max_accept = l->maxaccept ? l->maxaccept : 1;
+ int expire;
int cfd;
int ret;
#ifdef USE_ACCEPT4
@@ -270,14 +291,11 @@
if (!(l->options & LI_O_UNLIMITED) && global.sps_lim) {
int max = freq_ctr_remain(&global.sess_per_sec, global.sps_lim, 0);
- int expire;
if (unlikely(!max)) {
/* frontend accept rate limit was reached */
- limit_listener(l, &global_listener_queue);
expire = tick_add(now_ms, next_event_delay(&global.sess_per_sec, global.sps_lim, 0));
- task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
- return;
+ goto wait_expire;
}
if (max_accept > max)
@@ -286,14 +304,11 @@
if (!(l->options & LI_O_UNLIMITED) && global.cps_lim) {
int max = freq_ctr_remain(&global.conn_per_sec, global.cps_lim, 0);
- int expire;
if (unlikely(!max)) {
/* frontend accept rate limit was reached */
- limit_listener(l, &global_listener_queue);
expire = tick_add(now_ms, next_event_delay(&global.conn_per_sec, global.cps_lim, 0));
- task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
- return;
+ goto wait_expire;
}
if (max_accept > max)
@@ -302,14 +317,11 @@
#ifdef USE_OPENSSL
if (!(l->options & LI_O_UNLIMITED) && global.ssl_lim && l->bind_conf && l->bind_conf->is_ssl) {
int max = freq_ctr_remain(&global.ssl_per_sec, global.ssl_lim, 0);
- int expire;
if (unlikely(!max)) {
/* frontend accept rate limit was reached */
- limit_listener(l, &global_listener_queue);
expire = tick_add(now_ms, next_event_delay(&global.ssl_per_sec, global.ssl_lim, 0));
- task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
- return;
+ goto wait_expire;
}
if (max_accept > max)
@@ -365,8 +377,20 @@
if (unlikely(cfd == -1)) {
switch (errno) {
case EAGAIN:
+ if (fdtab[fd].ev & FD_POLL_HUP) {
+ /* the listening socket might have been disabled in a shared
+ * process and we're a collateral victim. We'll just pause for
+ * a while in case it comes back. In the mean time, we need to
+ * clear this sticky flag.
+ */
+ fdtab[fd].ev &= ~FD_POLL_HUP;
+ goto transient_error;
+ }
fd_cant_recv(fd);
return; /* nothing more to accept */
+ case EINVAL:
+ /* might be trying to accept on a shut fd (eg: soft stop) */
+ goto transient_error;
case EINTR:
case ECONNABORTED:
continue;
@@ -375,26 +399,20 @@
send_log(p, LOG_EMERG,
"Proxy %s reached system FD limit at %d. Please check system tunables.\n",
p->id, maxfd);
- limit_listener(l, &global_listener_queue);
- task_schedule(global_listener_queue_task, tick_add(now_ms, 100)); /* try again in 100 ms */
- return;
+ goto transient_error;
case EMFILE:
if (p)
send_log(p, LOG_EMERG,
"Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
p->id, maxfd);
- limit_listener(l, &global_listener_queue);
- task_schedule(global_listener_queue_task, tick_add(now_ms, 100)); /* try again in 100 ms */
- return;
+ goto transient_error;
case ENOBUFS:
case ENOMEM:
if (p)
send_log(p, LOG_EMERG,
"Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
p->id, maxfd);
- limit_listener(l, &global_listener_queue);
- task_schedule(global_listener_queue_task, tick_add(now_ms, 100)); /* try again in 100 ms */
- return;
+ goto transient_error;
default:
/* unexpected result, let's give up and let other tasks run */
goto stop;
@@ -442,9 +460,7 @@
if (ret == 0) /* successful termination */
continue;
- limit_listener(l, &global_listener_queue);
- task_schedule(global_listener_queue_task, tick_add(now_ms, 100)); /* try again in 100 ms */
- return;
+ goto transient_error;
}
if (l->nbconn >= l->maxconn) {
@@ -473,6 +489,15 @@
stop:
fd_done_recv(fd);
return;
+
+ transient_error:
+ /* pause the listener and try again in 100 ms */
+ expire = tick_add(now_ms, 100);
+
+ wait_expire:
+ limit_listener(l, &global_listener_queue);
+ task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
+ return;
}
/*
@@ -698,6 +723,49 @@
return 0;
}
+/* parse the "process" bind keyword */
+static int bind_parse_process(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ unsigned long set = 0;
+ unsigned int low, high;
+
+ if (strcmp(args[cur_arg + 1], "all") == 0) {
+ set = 0;
+ }
+ else if (strcmp(args[cur_arg + 1], "odd") == 0) {
+ set |= ~0UL/3UL; /* 0x555....555 */
+ }
+ else if (strcmp(args[cur_arg + 1], "even") == 0) {
+ set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
+ }
+ else if (isdigit((int)*args[cur_arg + 1])) {
+ char *dash = strchr(args[cur_arg + 1], '-');
+
+ low = high = str2uic(args[cur_arg + 1]);
+ if (dash)
+ high = str2uic(dash + 1);
+
+ if (high < low) {
+ unsigned int swap = low;
+ low = high;
+ high = swap;
+ }
+
+ if (low < 1 || high > LONGBITS) {
+ memprintf(err, "'%s' : invalid range %d-%d, allowed range is 1..%d", args[cur_arg], low, high, LONGBITS);
+ return ERR_ALERT | ERR_FATAL;
+ }
+ while (low <= high)
+ set |= 1UL << (low++ - 1);
+ }
+ else {
+ memprintf(err, "'%s' expects 'all', 'odd', 'even', or a process range with numbers from 1 to %d.", args[cur_arg], LONGBITS);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ conf->bind_proc = set;
+ return 0;
+}
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
@@ -729,6 +797,7 @@
{ "maxconn", bind_parse_maxconn, 1 }, /* set maxconn of listening socket */
{ "name", bind_parse_name, 1 }, /* set name of listening socket */
{ "nice", bind_parse_nice, 1 }, /* set nice of listening socket */
+ { "process", bind_parse_process, 1 }, /* set list of allowed process for this socket */
{ /* END */ },
}};
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/map.c
^
|
@@ -153,7 +153,7 @@
}
/* Load map. */
- if (!pattern_read_from_file(&desc->pat, PAT_REF_MAP, arg[0].data.str.str, PAT_F_NO_DNS,
+ if (!pattern_read_from_file(&desc->pat, PAT_REF_MAP, arg[0].data.str.str, PAT_MF_NO_DNS,
1, err, file, line))
return 0;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/pattern.c
^
|
@@ -41,7 +41,7 @@
[PAT_MATCH_REG] = "reg",
};
-int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, char **) = {
+int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, int, char **) = {
[PAT_MATCH_FOUND] = pat_parse_nothing,
[PAT_MATCH_BOOL] = pat_parse_nothing,
[PAT_MATCH_INT] = pat_parse_int,
@@ -65,7 +65,7 @@
[PAT_MATCH_BIN] = pat_idx_list_ptr,
[PAT_MATCH_LEN] = pat_idx_list_val,
[PAT_MATCH_STR] = pat_idx_tree_str,
- [PAT_MATCH_BEG] = pat_idx_list_str,
+ [PAT_MATCH_BEG] = pat_idx_tree_pfx,
[PAT_MATCH_SUB] = pat_idx_list_str,
[PAT_MATCH_DIR] = pat_idx_list_str,
[PAT_MATCH_DOM] = pat_idx_list_str,
@@ -81,7 +81,7 @@
[PAT_MATCH_BIN] = pat_del_list_ptr,
[PAT_MATCH_LEN] = pat_del_list_val,
[PAT_MATCH_STR] = pat_del_tree_str,
- [PAT_MATCH_BEG] = pat_del_list_ptr,
+ [PAT_MATCH_BEG] = pat_del_tree_str,
[PAT_MATCH_SUB] = pat_del_list_ptr,
[PAT_MATCH_DIR] = pat_del_list_ptr,
[PAT_MATCH_DOM] = pat_del_list_ptr,
@@ -194,13 +194,13 @@
*/
/* ignore the current line */
-int pat_parse_nothing(const char *text, struct pattern *pattern, char **err)
+int pat_parse_nothing(const char *text, struct pattern *pattern, int mflags, char **err)
{
return 1;
}
/* Parse a string. It is allocated and duplicated. */
-int pat_parse_str(const char *text, struct pattern *pattern, char **err)
+int pat_parse_str(const char *text, struct pattern *pattern, int mflags, char **err)
{
pattern->type = SMP_T_STR;
pattern->ptr.str = (char *)text;
@@ -209,7 +209,7 @@
}
/* Parse a binary written in hexa. It is allocated. */
-int pat_parse_bin(const char *text, struct pattern *pattern, char **err)
+int pat_parse_bin(const char *text, struct pattern *pattern, int mflags, char **err)
{
struct chunk *trash;
@@ -221,7 +221,7 @@
}
/* Parse a regex. It is allocated. */
-int pat_parse_reg(const char *text, struct pattern *pattern, char **err)
+int pat_parse_reg(const char *text, struct pattern *pattern, int mflags, char **err)
{
struct chunk *trash;
@@ -252,7 +252,7 @@
* non-zero on success.
*
*/
-int pat_parse_int(const char *text, struct pattern *pattern, char **err)
+int pat_parse_int(const char *text, struct pattern *pattern, int mflags, char **err)
{
const char *ptr = text;
@@ -337,7 +337,7 @@
* acl valid_ssl ssl_req_proto 3.0-3.1
*
*/
-int pat_parse_dotted_ver(const char *text, struct pattern *pattern, char **err)
+int pat_parse_dotted_ver(const char *text, struct pattern *pattern, int mflags, char **err)
{
const char *ptr = text;
@@ -404,9 +404,9 @@
* may either be a dotted mask or a number of bits. Returns 1 if OK,
* otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6).
*/
-int pat_parse_ip(const char *text, struct pattern *pattern, char **err)
+int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **err)
{
- if (str2net(text, !(pattern->flags & PAT_F_NO_DNS) && (global.mode & MODE_STARTING),
+ if (str2net(text, !(mflags & PAT_MF_NO_DNS) && (global.mode & MODE_STARTING),
&pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
pattern->type = SMP_T_IPV4;
return 1;
@@ -438,7 +438,6 @@
if (fill) {
static_pattern.smp = NULL;
static_pattern.ref = NULL;
- static_pattern.flags = 0;
static_pattern.type = 0;
static_pattern.ptr.str = NULL;
}
@@ -474,7 +473,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_STR;
static_pattern.ptr.str = (char *)elt->node.key;
}
@@ -489,7 +488,7 @@
if (pattern->len != smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0))
return pattern;
@@ -540,16 +539,43 @@
struct pattern *pat_match_beg(struct sample *smp, struct pattern_expr *expr, int fill)
{
int icase;
+ struct ebmb_node *node;
+ char prev;
+ struct pattern_tree *elt;
struct pattern_list *lst;
struct pattern *pattern;
+ /* Lookup a string in the expression's pattern tree. */
+ if (!eb_is_empty(&expr->pattern_tree)) {
+ /* we may have to force a trailing zero on the test pattern */
+ prev = smp->data.str.str[smp->data.str.len];
+ if (prev)
+ smp->data.str.str[smp->data.str.len] = '\0';
+ node = ebmb_lookup_longest(&expr->pattern_tree, smp->data.str.str);
+ if (prev)
+ smp->data.str.str[smp->data.str.len] = prev;
+
+ if (node) {
+ if (fill) {
+ elt = ebmb_entry(node, struct pattern_tree, node);
+ static_pattern.smp = elt->smp;
+ static_pattern.ref = elt->ref;
+ static_pattern.sflags = PAT_SF_TREE;
+ static_pattern.type = SMP_T_STR;
+ static_pattern.ptr.str = (char *)elt->node.key;
+ }
+ return &static_pattern;
+ }
+ }
+
+ /* look in the list */
list_for_each_entry(lst, &expr->patterns, list) {
pattern = &lst->pat;
if (pattern->len > smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0))
continue;
@@ -572,7 +598,7 @@
if (pattern->len > smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0))
continue;
@@ -600,7 +626,7 @@
continue;
end = smp->data.str.str + smp->data.str.len - pattern->len;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if (icase) {
for (c = smp->data.str.str; c <= end; c++) {
if (tolower(*c) != tolower(*pattern->ptr.str))
@@ -626,7 +652,7 @@
* provided as an unsigned int made by make_4delim() and match up to 4 different
* delimiters. Delimiters are stripped at the beginning and end of the pattern.
*/
-static int match_word(struct sample *smp, struct pattern *pattern, unsigned int delimiters)
+static int match_word(struct sample *smp, struct pattern *pattern, int mflags, unsigned int delimiters)
{
int may_match, icase;
char *c, *end;
@@ -648,7 +674,7 @@
return PAT_NOMATCH;
may_match = 1;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = mflags & PAT_MF_IGNORE_CASE;
end = smp->data.str.str + smp->data.str.len - pl;
for (c = smp->data.str.str; c <= end; c++) {
if (is_delimiter(*c, delimiters)) {
@@ -686,7 +712,7 @@
list_for_each_entry(lst, &expr->patterns, list) {
pattern = &lst->pat;
- if (match_word(smp, pattern, make_4delim('/', '?', '?', '?')))
+ if (match_word(smp, pattern, expr->mflags, make_4delim('/', '?', '?', '?')))
return pattern;
}
return NULL;
@@ -703,7 +729,7 @@
list_for_each_entry(lst, &expr->patterns, list) {
pattern = &lst->pat;
- if (match_word(smp, pattern, make_4delim('/', '?', '.', ':')))
+ if (match_word(smp, pattern, expr->mflags, make_4delim('/', '?', '.', ':')))
return pattern;
}
return NULL;
@@ -761,7 +787,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV4;
memcpy(&static_pattern.val.ipv4.addr.s_addr, elt->node.key, 4);
if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
@@ -783,7 +809,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV6;
memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
static_pattern.val.ipv6.mask = elt->node.node.pfx;
@@ -803,7 +829,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV6;
memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
static_pattern.val.ipv6.mask = elt->node.node.pfx;
@@ -837,7 +863,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV4;
memcpy(&static_pattern.val.ipv4.addr.s_addr, elt->node.key, 4);
if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
@@ -1049,7 +1075,7 @@
}
/* compile regex */
- if (!regex_comp(pat->ptr.str, patl->pat.ptr.reg, !(patl->pat.flags & PAT_F_IGNORE_CASE), 0, err)) {
+ if (!regex_comp(pat->ptr.str, patl->pat.ptr.reg, !(expr->mflags & PAT_MF_IGNORE_CASE), 0, err)) {
free(patl);
free(patl->pat.ptr.reg);
return 0;
@@ -1143,7 +1169,7 @@
}
/* If the flag PAT_F_IGNORE_CASE is set, we cannot use trees */
- if (pat->flags & PAT_F_IGNORE_CASE)
+ if (expr->mflags & PAT_MF_IGNORE_CASE)
return pat_idx_list_str(expr, pat, err);
/* Process the key len */
@@ -1170,6 +1196,47 @@
return 1;
}
+int pat_idx_tree_pfx(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+ int len;
+ struct pattern_tree *node;
+
+ /* Only string can be indexed */
+ if (pat->type != SMP_T_STR) {
+ memprintf(err, "internal error: string expected, but the type is '%s'",
+ smp_to_type[pat->type]);
+ return 0;
+ }
+
+ /* If the flag PAT_F_IGNORE_CASE is set, we cannot use trees */
+ if (expr->mflags & PAT_MF_IGNORE_CASE)
+ return pat_idx_list_str(expr, pat, err);
+
+ /* Process the key len */
+ len = strlen(pat->ptr.str);
+
+ /* node memory allocation */
+ node = calloc(1, sizeof(*node) + len + 1);
+ if (!node) {
+ memprintf(err, "out of memory while loading pattern");
+ return 0;
+ }
+
+ /* copy the pointer to sample associated to this node */
+ node->smp = pat->smp;
+ node->ref = pat->ref;
+
+ /* copy the string and the trailing zero */
+ memcpy(node->node.key, pat->ptr.str, len + 1);
+ node->node.node.pfx = len * 8;
+
+ /* index the new node */
+ ebmb_insert_prefix(&expr->pattern_tree, &node->node, len);
+
+ /* that's ok */
+ return 1;
+}
+
void pat_del_list_val(struct pattern_expr *expr, struct pat_ref_elt *ref)
{
struct pattern_list *pat;
@@ -1408,6 +1475,23 @@
return 1;
}
+/*
+ * find and return an element <elt> matching <key> in a reference <ref>
+ * return NULL if not found
+ */
+struct pat_ref_elt *pat_ref_find_elt(struct pat_ref *ref, const char *key)
+{
+ struct pat_ref_elt *elt;
+
+ list_for_each_entry(elt, &ref->head, list) {
+ if (strcmp(key, elt->pattern) == 0)
+ return elt;
+ }
+
+ return NULL;
+}
+
+
/* This function modify the sample of the first pattern that match the <key>. */
static inline int pat_ref_set_elt(struct pat_ref *ref, struct pat_ref_elt *elt,
const char *value, char **err)
@@ -1653,12 +1737,11 @@
/* initialise pattern */
memset(&pattern, 0, sizeof(pattern));
- pattern.flags = patflags;
pattern.smp = smp;
pattern.ref = elt;
/* parse pattern */
- if (!expr->pat_head->parse(elt->pattern, &pattern, err)) {
+ if (!expr->pat_head->parse(elt->pattern, &pattern, expr->mflags, err)) {
free(smp);
return 0;
}
@@ -2091,10 +2174,11 @@
* doesn't exists, create it.
*/
expr = pattern_lookup_expr(head, ref);
- if (!expr) {
+ if (!expr || (expr->mflags != patflags)) {
expr = pattern_new_expr(head, ref, err);
if (!expr)
return 0;
+ expr->mflags = patflags;
}
/* Load reference content in the pattern expression. */
@@ -2125,7 +2209,7 @@
if (fill) {
static_pattern.smp = NULL;
static_pattern.ref = NULL;
- static_pattern.flags = 0;
+ static_pattern.sflags = 0;
static_pattern.type = SMP_T_UINT;
static_pattern.val.i = 1;
}
@@ -2223,7 +2307,7 @@
break;
}
}
- if (&ref2->list == &pattern_reference);
+ if (&ref2->list == &pattern_reference)
break;
}
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/proto_http.c
^
|
@@ -64,6 +64,7 @@
#include <proto/session.h>
#include <proto/stream_interface.h>
#include <proto/task.h>
+#include <proto/pattern.h>
const char HTTP_100[] =
"HTTP/1.1 100 Continue\r\n\r\n";
@@ -216,6 +217,16 @@
};
+/* List head of all known action keywords for "http-request" */
+struct http_req_action_kw_list http_req_keywords = {
+ .list = LIST_HEAD_INIT(http_req_keywords.list)
+};
+
+/* List head of all known action keywords for "http-response" */
+struct http_res_action_kw_list http_res_keywords = {
+ .list = LIST_HEAD_INIT(http_res_keywords.list)
+};
+
/* We must put the messages here since GCC cannot initialize consts depending
* on strlen().
*/
@@ -241,6 +252,8 @@
#error "Check if your OS uses bitfields for fd_sets"
#endif
+static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *s, struct http_txn *txn);
+
void init_proto_http()
{
int i;
@@ -746,7 +759,7 @@
* The error flags are set to the values in arguments. Any pending request
* in this buffer will be lost.
*/
-static void http_server_error(struct session *t, struct stream_interface *si,
+static void http_server_error(struct session *s, struct stream_interface *si,
int err, int finst, int status, const struct chunk *msg)
{
channel_auto_read(si->ob);
@@ -756,13 +769,13 @@
channel_auto_close(si->ib);
channel_auto_read(si->ib);
if (status > 0 && msg) {
- t->txn.status = status;
+ s->txn.status = status;
bo_inject(si->ib, msg->str, msg->len);
}
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= err;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= finst;
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= err;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= finst;
}
/* This function returns the appropriate error location for the given session
@@ -2905,6 +2918,70 @@
if (!use_close_only)
msg->flags |= HTTP_MSGF_XFER_LEN;
+ /* Until set to anything else, the connection mode is set as Keep-Alive. It will
+ * only change if both the request and the config reference something else.
+ * Option httpclose by itself sets tunnel mode where headers are mangled.
+ * However, if another mode is set, it will affect it (eg: server-close/
+ * keep-alive + httpclose = close). Note that we avoid to redo the same work
+ * if FE and BE have the same settings (common). The method consists in
+ * checking if options changed between the two calls (implying that either
+ * one is non-null, or one of them is non-null and we are there for the first
+ * time.
+ */
+ if (!(txn->flags & TX_HDR_CONN_PRS) ||
+ ((s->fe->options & PR_O_HTTP_MODE) != (s->be->options & PR_O_HTTP_MODE))) {
+ int tmp = TX_CON_WANT_KAL;
+
+ if (!((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)) {
+ if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN)
+ tmp = TX_CON_WANT_TUN;
+
+ if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL)
+ tmp = TX_CON_WANT_TUN;
+ }
+
+ if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL) {
+ /* option httpclose + server_close => forceclose */
+ if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL)
+ tmp = TX_CON_WANT_CLO;
+ else
+ tmp = TX_CON_WANT_SCL;
+ }
+
+ if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL)
+ tmp = TX_CON_WANT_CLO;
+
+ if ((txn->flags & TX_CON_WANT_MSK) < tmp)
+ txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | tmp;
+
+ if (!(txn->flags & TX_HDR_CONN_PRS) &&
+ (txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) {
+ /* parse the Connection header and possibly clean it */
+ int to_del = 0;
+ if ((msg->flags & HTTP_MSGF_VER_11) ||
+ ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL &&
+ !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)))
+ to_del |= 2; /* remove "keep-alive" */
+ if (!(msg->flags & HTTP_MSGF_VER_11))
+ to_del |= 1; /* remove "close" */
+ http_parse_connection_header(txn, msg, to_del);
+ }
+
+ /* check if client or config asks for explicit close in KAL/SCL */
+ if (((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) &&
+ ((txn->flags & TX_HDR_CONN_CLO) || /* "connection: close" */
+ (!(msg->flags & HTTP_MSGF_VER_11) && !(txn->flags & TX_HDR_CONN_KAL)) || /* no "connection: k-a" in 1.0 */
+ !(msg->flags & HTTP_MSGF_XFER_LEN) || /* no length known => close */
+ s->fe->state == PR_STSTOPPED)) /* frontend is stopping */
+ txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO;
+ }
+
/* end of job, return OK */
req->analysers &= ~an_bit;
req->analyse_exp = TICK_ETERNITY;
@@ -2962,6 +3039,8 @@
appctx->st1 = appctx->st2 = 0;
appctx->ctx.stats.st_code = STAT_STATUS_INIT;
appctx->ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
+ if ((msg->flags & HTTP_MSGF_VER_11) && (s->txn.meth != HTTP_METH_HEAD))
+ appctx->ctx.stats.flags |= STAT_CHUNKED;
uri = msg->chn->buf->p + msg->sl.rq.u;
lookup = uri + uri_auth->uri_len;
@@ -3059,22 +3138,8 @@
/* Was the status page requested with a POST ? */
if (unlikely(txn->meth == HTTP_METH_POST && txn->req.body_len > 0)) {
if (appctx->ctx.stats.flags & STAT_ADMIN) {
- if (msg->msg_state < HTTP_MSG_100_SENT) {
- /* If we have HTTP/1.1 and Expect: 100-continue, then we must
- * send an HTTP/1.1 100 Continue intermediate response.
- */
- if (msg->flags & HTTP_MSGF_VER_11) {
- struct hdr_ctx ctx;
- ctx.idx = 0;
- /* Expect is allowed in 1.1, look for it */
- if (http_find_header2("Expect", 6, req->buf->p, &txn->hdr_idx, &ctx) &&
- unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0)) {
- bo_inject(s->rep, http_100_chunk.str, http_100_chunk.len);
- }
- }
- msg->msg_state = HTTP_MSG_100_SENT;
- s->logs.tv_request = now; /* update the request timer to reflect full request */
- }
+ /* we'll need the request body, possibly after sending 100-continue */
+ req->analysers |= AN_REQ_HTTP_BODY;
appctx->st0 = STAT_HTTP_POST;
}
else {
@@ -3112,17 +3177,19 @@
}
/* Executes the http-request rules <rules> for session <s>, proxy <px> and
- * transaction <txn>. Returns the first rule that prevents further processing
- * of the request (auth, deny, ...) or NULL if it executed all rules or stopped
- * on an allow. It may set the TX_CLDENY on txn->flags if it encounters a deny
- * rule.
+ * transaction <txn>. Returns the verdict of the first rule that prevents
+ * further processing of the request (auth, deny, ...), and defaults to
+ * HTTP_RULE_RES_STOP if it executed all rules or stopped on an allow, or
+ * HTTP_RULE_RES_CONT if the last rule was reached. It may set the TX_CLTARPIT
+ * on txn->flags if it encounters a tarpit rule.
*/
-static struct http_req_rule *
+enum rule_result
http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session *s, struct http_txn *txn)
{
struct connection *cli_conn;
struct http_req_rule *rule;
struct hdr_ctx ctx;
+ const char *auth_realm;
list_for_each_entry(rule, rules, list) {
if (rule->action >= HTTP_REQ_ACT_MAX)
@@ -3145,21 +3212,38 @@
switch (rule->action) {
case HTTP_REQ_ACT_ALLOW:
- return NULL; /* "allow" rules are OK */
+ return HTTP_RULE_RES_STOP;
case HTTP_REQ_ACT_DENY:
- txn->flags |= TX_CLDENY;
- return rule;
+ return HTTP_RULE_RES_DENY;
case HTTP_REQ_ACT_TARPIT:
txn->flags |= TX_CLTARPIT;
- return rule;
+ return HTTP_RULE_RES_DENY;
case HTTP_REQ_ACT_AUTH:
- return rule;
+ /* Auth might be performed on regular http-req rules as well as on stats */
+ auth_realm = rule->arg.auth.realm;
+ if (!auth_realm) {
+ if (px->uri_auth && rules == &px->uri_auth->http_req_rules)
+ auth_realm = STATS_DEFAULT_REALM;
+ else
+ auth_realm = px->id;
+ }
+ /* send 401/407 depending on whether we use a proxy or not. We still
+ * count one error, because normal browsing won't significantly
+ * increase the counter but brute force attempts will.
+ */
+ chunk_printf(&trash, (txn->flags & TX_USE_PX_CONN) ? HTTP_407_fmt : HTTP_401_fmt, auth_realm);
+ txn->status = (txn->flags & TX_USE_PX_CONN) ? 407 : 401;
+ stream_int_retnclose(&s->si[0], &trash);
+ session_inc_http_err_ctr(s);
+ return HTTP_RULE_RES_ABRT;
case HTTP_REQ_ACT_REDIR:
- return rule;
+ if (!http_apply_redirect_rule(rule->arg.redir, s, txn))
+ return HTTP_RULE_RES_BADREQ;
+ return HTTP_RULE_RES_DONE;
case HTTP_REQ_ACT_SET_NICE:
s->task->nice = rule->arg.nice;
@@ -3202,11 +3286,103 @@
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->req, &txn->hdr_idx, trash.str, trash.len);
break;
+
+ case HTTP_REQ_ACT_DEL_ACL:
+ case HTTP_REQ_ACT_DEL_MAP: {
+ struct pat_ref *ref;
+ char *key;
+ int len;
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
+ key = trash.str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* returned code: 1=ok, 0=ko */
+ pat_ref_delete(ref, key);
+
+ break;
+ }
+
+ case HTTP_REQ_ACT_ADD_ACL: {
+ struct pat_ref *ref;
+ char *key;
+ struct chunk *trash_key;
+ int len;
+
+ trash_key = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* add entry only if it does not already exist */
+ if (pat_ref_find_elt(ref, key) == NULL)
+ pat_ref_add(ref, key, NULL, NULL);
+
+ break;
+ }
+
+ case HTTP_REQ_ACT_SET_MAP: {
+ struct pat_ref *ref;
+ char *key, *value;
+ struct chunk *trash_key, *trash_value;
+ int len;
+
+ trash_key = get_trash_chunk();
+ trash_value = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* collect value */
+ len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
+ value = trash_value->str;
+ value[len] = '\0';
+
+ /* perform update */
+ if (pat_ref_find_elt(ref, key) != NULL)
+ /* update entry if it exists */
+ pat_ref_set(ref, key, value, NULL);
+ else
+ /* insert a new entry */
+ pat_ref_add(ref, key, value, NULL);
+
+ break;
+ }
+
+ case HTTP_REQ_ACT_CUSTOM_CONT:
+ rule->action_ptr(rule, px, s, txn);
+ break;
+
+ case HTTP_REQ_ACT_CUSTOM_STOP:
+ rule->action_ptr(rule, px, s, txn);
+ return HTTP_RULE_RES_DONE;
}
}
/* we reached the end of the rules, nothing to report */
- return NULL;
+ return HTTP_RULE_RES_CONT;
}
@@ -3291,6 +3467,98 @@
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len);
break;
+
+ case HTTP_RES_ACT_DEL_ACL:
+ case HTTP_RES_ACT_DEL_MAP: {
+ struct pat_ref *ref;
+ char *key;
+ int len;
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
+ key = trash.str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* returned code: 1=ok, 0=ko */
+ pat_ref_delete(ref, key);
+
+ break;
+ }
+
+ case HTTP_RES_ACT_ADD_ACL: {
+ struct pat_ref *ref;
+ char *key;
+ struct chunk *trash_key;
+ int len;
+
+ trash_key = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* check if the entry already exists */
+ if (pat_ref_find_elt(ref, key) == NULL)
+ pat_ref_add(ref, key, NULL, NULL);
+
+ break;
+ }
+
+ case HTTP_RES_ACT_SET_MAP: {
+ struct pat_ref *ref;
+ char *key, *value;
+ struct chunk *trash_key, *trash_value;
+ int len;
+
+ trash_key = get_trash_chunk();
+ trash_value = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* collect value */
+ len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
+ value = trash_value->str;
+ value[len] = '\0';
+
+ /* perform update */
+ if (pat_ref_find_elt(ref, key) != NULL)
+ /* update entry if it exists */
+ pat_ref_set(ref, key, value, NULL);
+ else
+ /* insert a new entry */
+ pat_ref_add(ref, key, value, NULL);
+
+ break;
+ }
+
+ case HTTP_RES_ACT_CUSTOM_CONT:
+ rule->action_ptr(rule, px, s, txn);
+ break;
+
+ case HTTP_RES_ACT_CUSTOM_STOP:
+ rule->action_ptr(rule, px, s, txn);
+ return rule;
}
}
@@ -3528,6 +3796,7 @@
bo_inject(txn->rsp.chn, trash.str, trash.len);
/* "eat" the request */
bi_fast_delete(txn->req.chn->buf, msg->sov);
+ msg->next -= msg->sov;
msg->sov = 0;
txn->req.chn->analysers = AN_REQ_HTTP_XFER_BODY;
s->rep->analysers = AN_RES_HTTP_XFER_BODY;
@@ -3565,10 +3834,9 @@
{
struct http_txn *txn = &s->txn;
struct http_msg *msg = &txn->req;
- struct acl_cond *cond;
- struct http_req_rule *http_req_last_rule = NULL;
struct redirect_rule *rule;
struct cond_wordlist *wl;
+ enum rule_result verdict;
if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
/* we need more data */
@@ -3576,9 +3844,6 @@
return 0;
}
- req->analysers &= ~an_bit;
- req->analyse_exp = TICK_ETERNITY;
-
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
now_ms, __FUNCTION__,
s,
@@ -3588,181 +3853,74 @@
req->buf->i,
req->analysers);
- /* first check whether we have some ACLs set to block this request */
- list_for_each_entry(cond, &px->block_cond, list) {
- int ret = acl_exec_cond(cond, px, s, txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
-
- ret = acl_pass(ret);
- if (cond->pol == ACL_COND_UNLESS)
- ret = !ret;
-
- if (ret) {
- txn->status = 403;
- /* let's log the request time */
- s->logs.tv_request = now;
- stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_403));
- session_inc_http_err_ctr(s);
- goto return_prx_cond;
- }
- }
-
/* just in case we have some per-backend tracking */
session_inc_be_http_req_ctr(s);
/* evaluate http-request rules */
- http_req_last_rule = http_req_get_intercept_rule(px, &px->http_req_rules, s, txn);
+ if (!LIST_ISEMPTY(&px->http_req_rules)) {
+ verdict = http_req_get_intercept_rule(px, &px->http_req_rules, s, txn);
- /* evaluate stats http-request rules only if http-request is OK */
- if (!http_req_last_rule) {
- if (stats_check_uri(s->rep->prod, txn, px)) {
- s->target = &http_stats_applet.obj_type;
- if (unlikely(!stream_int_register_handler(s->rep->prod, objt_applet(s->target)))) {
- txn->status = 500;
- s->logs.tv_request = now;
- stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_500));
-
- if (!(s->flags & SN_ERR_MASK))
- s->flags |= SN_ERR_RESOURCE;
- goto return_prx_cond;
- }
- /* parse the whole stats request and extract the relevant information */
- http_handle_stats(s, req);
- http_req_last_rule = http_req_get_intercept_rule(px, &px->uri_auth->http_req_rules, s, txn);
- }
- }
+ switch (verdict) {
+ case HTTP_RULE_RES_CONT:
+ case HTTP_RULE_RES_STOP: /* nothing to do */
+ break;
- /* only apply req{,i}{rep/deny/tarpit} if the request was not yet
- * blocked by an http-request rule.
- */
- if (!(txn->flags & (TX_CLDENY|TX_CLTARPIT)) && (px->req_exp != NULL)) {
- if (apply_filters_to_request(s, req, px) < 0)
- goto return_bad_req;
- }
+ case HTTP_RULE_RES_DENY: /* deny or tarpit */
+ if (txn->flags & TX_CLTARPIT)
+ goto tarpit;
+ goto deny;
- /* return a 403 if either rule has blocked */
- if (txn->flags & (TX_CLDENY|TX_CLTARPIT)) {
- if (txn->flags & TX_CLDENY) {
- txn->status = 403;
- s->logs.tv_request = now;
- stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_403));
- session_inc_http_err_ctr(s);
- s->fe->fe_counters.denied_req++;
- if (s->fe != s->be)
- s->be->be_counters.denied_req++;
- if (s->listener->counters)
- s->listener->counters->denied_req++;
+ case HTTP_RULE_RES_ABRT: /* abort request, response already sent. Eg: auth */
goto return_prx_cond;
- }
- /* When a connection is tarpitted, we use the tarpit timeout,
- * which may be the same as the connect timeout if unspecified.
- * If unset, then set it to zero because we really want it to
- * eventually expire. We build the tarpit as an analyser.
- */
- if (txn->flags & TX_CLTARPIT) {
- channel_erase(s->req);
- /* wipe the request out so that we can drop the connection early
- * if the client closes first.
- */
- channel_dont_connect(req);
- req->analysers = 0; /* remove switching rules etc... */
- req->analysers |= AN_REQ_HTTP_TARPIT;
- req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
- if (!req->analyse_exp)
- req->analyse_exp = tick_add(now_ms, 0);
- session_inc_http_err_ctr(s);
- s->fe->fe_counters.denied_req++;
- if (s->fe != s->be)
- s->be->be_counters.denied_req++;
- if (s->listener->counters)
- s->listener->counters->denied_req++;
- return 1;
+ case HTTP_RULE_RES_DONE: /* OK, but terminate request processing (eg: redirect) */
+ goto done;
+
+ case HTTP_RULE_RES_BADREQ: /* failed with a bad request */
+ goto return_bad_req;
}
}
- /* Until set to anything else, the connection mode is set as Keep-Alive. It will
- * only change if both the request and the config reference something else.
- * Option httpclose by itself sets tunnel mode where headers are mangled.
- * However, if another mode is set, it will affect it (eg: server-close/
- * keep-alive + httpclose = close). Note that we avoid to redo the same work
- * if FE and BE have the same settings (common). The method consists in
- * checking if options changed between the two calls (implying that either
- * one is non-null, or one of them is non-null and we are there for the first
- * time.
- */
-
- if (!(txn->flags & TX_HDR_CONN_PRS) ||
- ((s->fe->options & PR_O_HTTP_MODE) != (s->be->options & PR_O_HTTP_MODE))) {
- int tmp = TX_CON_WANT_KAL;
-
- if (!((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)) {
- if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN ||
- (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN)
- tmp = TX_CON_WANT_TUN;
-
- if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
- (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL)
- tmp = TX_CON_WANT_TUN;
- }
+ /* OK at this stage, we know that the request was accepted according to
+ * the http-request rules, we can check for the stats. Note that the
+ * URI is detected *before* the req* rules in order not to be affected
+ * by a possible reqrep, while they are processed *after* so that a
+ * reqdeny can still block them. This clearly needs to change in 1.6!
+ */
+ if (stats_check_uri(s->rep->prod, txn, px)) {
+ s->target = &http_stats_applet.obj_type;
+ if (unlikely(!stream_int_register_handler(s->rep->prod, objt_applet(s->target)))) {
+ txn->status = 500;
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_500));
- if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL ||
- (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL) {
- /* option httpclose + server_close => forceclose */
- if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
- (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL)
- tmp = TX_CON_WANT_CLO;
- else
- tmp = TX_CON_WANT_SCL;
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_RESOURCE;
+ goto return_prx_cond;
}
- if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL ||
- (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL)
- tmp = TX_CON_WANT_CLO;
-
- if ((txn->flags & TX_CON_WANT_MSK) < tmp)
- txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | tmp;
+ /* parse the whole stats request and extract the relevant information */
+ http_handle_stats(s, req);
+ verdict = http_req_get_intercept_rule(px, &px->uri_auth->http_req_rules, s, txn);
+ /* not all actions implemented: deny, allow, auth */
- if (!(txn->flags & TX_HDR_CONN_PRS) &&
- (txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) {
- /* parse the Connection header and possibly clean it */
- int to_del = 0;
- if ((msg->flags & HTTP_MSGF_VER_11) ||
- ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL &&
- !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)))
- to_del |= 2; /* remove "keep-alive" */
- if (!(msg->flags & HTTP_MSGF_VER_11))
- to_del |= 1; /* remove "close" */
- http_parse_connection_header(txn, msg, to_del);
- }
+ if (verdict == HTTP_RULE_RES_DENY) /* stats http-request deny */
+ goto deny;
- /* check if client or config asks for explicit close in KAL/SCL */
- if (((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) &&
- ((txn->flags & TX_HDR_CONN_CLO) || /* "connection: close" */
- (!(msg->flags & HTTP_MSGF_VER_11) && !(txn->flags & TX_HDR_CONN_KAL)) || /* no "connection: k-a" in 1.0 */
- !(msg->flags & HTTP_MSGF_XFER_LEN) || /* no length known => close */
- s->fe->state == PR_STSTOPPED)) /* frontend is stopping */
- txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO;
+ if (verdict == HTTP_RULE_RES_ABRT) /* stats auth / stats http-request auth */
+ goto return_prx_cond;
}
- /* we can be blocked here because the request needs to be authenticated,
- * either to pass or to access stats.
- */
- if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_AUTH) {
- char *realm = http_req_last_rule->arg.auth.realm;
+ /* evaluate the req* rules except reqadd */
+ if (px->req_exp != NULL) {
+ if (apply_filters_to_request(s, req, px) < 0)
+ goto return_bad_req;
- if (!realm)
- realm = (objt_applet(s->target) == &http_stats_applet) ? STATS_DEFAULT_REALM : px->id;
+ if (txn->flags & TX_CLDENY)
+ goto deny;
- chunk_printf(&trash, (txn->flags & TX_USE_PX_CONN) ? HTTP_407_fmt : HTTP_401_fmt, realm);
- txn->status = 401;
- stream_int_retnclose(req->prod, &trash);
- /* on 401 we still count one error, because normal browsing
- * won't significantly increase the counter but brute force
- * attempts will.
- */
- session_inc_http_err_ctr(s);
- goto return_prx_cond;
+ if (txn->flags & TX_CLTARPIT)
+ goto tarpit;
}
/* add request headers from the rule sets in the same order */
@@ -3780,13 +3938,8 @@
goto return_bad_req;
}
- if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_REDIR) {
- if (!http_apply_redirect_rule(http_req_last_rule->arg.redir, s, txn))
- goto return_bad_req;
- req->analyse_exp = TICK_ETERNITY;
- return 1;
- }
+ /* Proceed with the stats now. */
if (unlikely(objt_applet(s->target) == &http_stats_applet)) {
/* process the stats request now */
if (s->fe == s->be) /* report it if the request was intercepted by the frontend */
@@ -3797,9 +3950,14 @@
if (!(s->flags & SN_FINST_MASK))
s->flags |= SN_FINST_R;
- req->analyse_exp = TICK_ETERNITY;
- req->analysers = 0;
- return 1;
+ /* we may want to compress the stats page */
+ if (s->fe->comp || s->be->comp)
+ select_compression_request_header(s, req->buf);
+
+ /* enable the minimally required analyzers to handle keep-alive and compression on the HTTP response */
+ req->analysers = (req->analysers & AN_REQ_HTTP_BODY) |
+ AN_REQ_HTTP_XFER_BODY | AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE | AN_RES_HTTP_XFER_BODY;
+ goto done;
}
/* check whether we have some ACLs set to redirect this request */
@@ -3816,9 +3974,7 @@
}
if (!http_apply_redirect_rule(rule, s, txn))
goto return_bad_req;
-
- req->analyse_exp = TICK_ETERNITY;
- return 1;
+ goto done;
}
/* POST requests may be accompanied with an "Expect: 100-Continue" header.
@@ -3830,9 +3986,51 @@
*/
req->flags |= CF_SEND_DONTWAIT;
- /* that's OK for us now, let's move on to next analysers */
+ done: /* done with this analyser, continue with next ones that the calling
+ * points will have set, if any.
+ */
+ req->analysers &= ~an_bit;
+ req->analyse_exp = TICK_ETERNITY;
return 1;
+ tarpit:
+ /* When a connection is tarpitted, we use the tarpit timeout,
+ * which may be the same as the connect timeout if unspecified.
+ * If unset, then set it to zero because we really want it to
+ * eventually expire. We build the tarpit as an analyser.
+ */
+ channel_erase(s->req);
+
+ /* wipe the request out so that we can drop the connection early
+ * if the client closes first.
+ */
+ channel_dont_connect(req);
+ req->analysers = 0; /* remove switching rules etc... */
+ req->analysers |= AN_REQ_HTTP_TARPIT;
+ req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
+ if (!req->analyse_exp)
+ req->analyse_exp = tick_add(now_ms, 0);
+ session_inc_http_err_ctr(s);
+ s->fe->fe_counters.denied_req++;
+ if (s->fe != s->be)
+ s->be->be_counters.denied_req++;
+ if (s->listener->counters)
+ s->listener->counters->denied_req++;
+ goto done;
+
+ deny: /* this request was blocked (denied) */
+ txn->flags |= TX_CLDENY;
+ txn->status = 403;
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_403));
+ session_inc_http_err_ctr(s);
+ s->fe->fe_counters.denied_req++;
+ if (s->fe != s->be)
+ s->be->be_counters.denied_req++;
+ if (s->listener->counters)
+ s->listener->counters->denied_req++;
+ goto return_prx_cond;
+
return_bad_req:
/* We centralize bad requests processing here */
if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) {
@@ -4556,7 +4754,7 @@
s->req->cons->conn_retries = 0; /* used for logging too */
s->req->cons->exp = TICK_ETERNITY;
s->req->cons->flags &= SI_FL_DONT_WAKE; /* we're in the context of process_session */
- s->req->flags &= ~(CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CONNECT|CF_WRITE_ERROR|CF_STREAMER|CF_STREAMER_FAST|CF_NEVER_WAIT);
+ s->req->flags &= ~(CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CONNECT|CF_WRITE_ERROR|CF_STREAMER|CF_STREAMER_FAST|CF_NEVER_WAIT|CF_WAKE_CONNECT);
s->rep->flags &= ~(CF_SHUTR|CF_SHUTR_NOW|CF_READ_ATTACHED|CF_READ_ERROR|CF_READ_NOEXP|CF_STREAMER|CF_STREAMER_FAST|CF_WRITE_PARTIAL|CF_NEVER_WAIT);
s->flags &= ~(SN_DIRECT|SN_ASSIGNED|SN_ADDR_SET|SN_BE_ASSIGNED|SN_FORCE_PRST|SN_IGNORE_PRST);
s->flags &= ~(SN_CURR_SESS|SN_REDIRECTABLE|SN_SRV_REUSED);
@@ -4977,6 +5175,13 @@
*/
msg->msg_state = HTTP_MSG_ERROR;
http_resync_states(s);
+
+ if (req->flags & CF_READ_TIMEOUT)
+ goto cli_timeout;
+
+ if (req->flags & CF_WRITE_TIMEOUT)
+ goto srv_timeout;
+
return 1;
}
@@ -5015,6 +5220,7 @@
if (unlikely(msg->flags & HTTP_MSGF_WAIT_CONN)) {
if (!(s->rep->flags & CF_READ_ATTACHED)) {
channel_auto_connect(req);
+ req->flags |= CF_WAKE_CONNECT;
goto missing_data;
}
msg->flags &= ~HTTP_MSGF_WAIT_CONN;
@@ -5023,6 +5229,12 @@
/* in most states, we should abort in case of early close */
channel_auto_close(req);
+ if (req->to_forward) {
+ /* We can't process the buffer's contents yet */
+ req->flags |= CF_WAKE_WRITE;
+ goto missing_data;
+ }
+
while (1) {
if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */
@@ -5136,6 +5348,11 @@
channel_auto_read(req);
}
+ /* if we received everything, we don't want to expire anymore */
+ if (msg->msg_state == HTTP_MSG_DONE) {
+ req->flags |= CF_READ_NOEXP;
+ req->rex = TICK_ETERNITY;
+ }
return 0;
}
}
@@ -5245,6 +5462,68 @@
s->flags |= SN_FINST_D;
}
return 0;
+
+ cli_timeout:
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_CLITO;
+
+ if (!(s->flags & SN_FINST_MASK)) {
+ if (txn->rsp.msg_state < HTTP_MSG_ERROR)
+ s->flags |= SN_FINST_H;
+ else
+ s->flags |= SN_FINST_D;
+ }
+
+ if (txn->status > 0) {
+ /* Don't send any error message if something was already sent */
+ stream_int_retnclose(req->prod, NULL);
+ }
+ else {
+ txn->status = 408;
+ stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_408));
+ }
+
+ msg->msg_state = HTTP_MSG_ERROR;
+ req->analysers = 0;
+ s->rep->analysers = 0; /* we're in data phase, we want to abort both directions */
+
+ session_inc_http_err_ctr(s);
+ s->fe->fe_counters.failed_req++;
+ s->be->be_counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+ return 0;
+
+ srv_timeout:
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_SRVTO;
+
+ if (!(s->flags & SN_FINST_MASK)) {
+ if (txn->rsp.msg_state < HTTP_MSG_ERROR)
+ s->flags |= SN_FINST_H;
+ else
+ s->flags |= SN_FINST_D;
+ }
+
+ if (txn->status > 0) {
+ /* Don't send any error message if something was already sent */
+ stream_int_retnclose(req->prod, NULL);
+ }
+ else {
+ txn->status = 504;
+ stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_504));
+ }
+
+ msg->msg_state = HTTP_MSG_ERROR;
+ req->analysers = 0;
+ s->rep->analysers = 0; /* we're in data phase, we want to abort both directions */
+
+ s->be->be_counters.failed_resp++;
+ if (objt_server(s->target)) {
+ objt_server(s->target)->counters.failed_resp++;
+ health_adjust(objt_server(s->target), HANA_STATUS_HTTP_READ_TIMEOUT);
+ }
+ return 0;
}
/* This stream analyser waits for a complete HTTP response. It returns 1 if the
@@ -5288,6 +5567,7 @@
* msg->next = first non-visited byte
*/
+ next_one:
/* There's a protected area at the end of the buffer for rewriting
* purposes. We don't want to start to parse the request if the
* protected area is affected, because we may have to move processed
@@ -5411,8 +5691,11 @@
return 0;
}
- /* read timeout : return a 504 to the client. */
- else if (rep->flags & CF_READ_TIMEOUT) {
+ /* read/write timeout : return a 504 to the client.
+ * The write timeout may happen when we're uploading POST
+ * data that the server is not consuming fast enough.
+ */
+ else if (rep->flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT)) {
if (msg->err_pos >= 0)
http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, s->fe);
else if (txn->flags & TX_NOT_FIRST)
@@ -5508,6 +5791,12 @@
return 0;
}
+ /* we don't want to expire on the server side first until the client
+ * has sent all the expected message body.
+ */
+ if (txn->req.msg_state >= HTTP_MSG_BODY && txn->req.msg_state < HTTP_MSG_DONE)
+ rep->flags |= CF_READ_NOEXP;
+
channel_dont_close(rep);
rep->flags |= CF_READ_DONTWAIT; /* try to get back here ASAP */
return 0;
@@ -5568,6 +5857,20 @@
*/
switch (txn->status) {
+ case 100:
+ /*
+ * We may be facing a 100-continue response, in which case this
+ * is not the right response, and we're waiting for the next one.
+ * Let's allow this response to go to the client and wait for the
+ * next one.
+ */
+ hdr_idx_init(&txn->hdr_idx);
+ msg->next -= channel_forward(rep, msg->next);
+ msg->msg_state = HTTP_MSG_RPBEFORE;
+ txn->status = 0;
+ s->logs.t_data = -1; /* was not a response yet */
+ goto next_one;
+
case 200:
case 203:
case 206:
@@ -5701,62 +6004,7 @@
if (s->fe->comp || s->be->comp)
select_compression_response_header(s, rep->buf);
- /* FIXME: we should also implement the multipart/byterange method.
- * For now on, we resort to close mode in this case (unknown length).
- */
skip_content_length:
-
- /* end of job, return OK */
- rep->analysers &= ~an_bit;
- rep->analyse_exp = TICK_ETERNITY;
- channel_auto_close(rep);
- return 1;
-
- abort_keep_alive:
- /* A keep-alive request to the server failed on a network error.
- * The client is required to retry. We need to close without returning
- * any other information so that the client retries.
- */
- txn->status = 0;
- rep->analysers = 0;
- s->req->analysers = 0;
- channel_auto_close(rep);
- s->logs.logwait = 0;
- s->logs.level = 0;
- s->rep->flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */
- bi_erase(rep);
- stream_int_retnclose(rep->cons, NULL);
- return 0;
-}
-
-/* This function performs all the processing enabled for the current response.
- * It normally returns 1 unless it wants to break. It relies on buffers flags,
- * and updates t->rep->analysers. It might make sense to explode it into several
- * other functions. It works like process_request (see indications above).
- */
-int http_process_res_common(struct session *t, struct channel *rep, int an_bit, struct proxy *px)
-{
- struct http_txn *txn = &t->txn;
- struct http_msg *msg = &txn->rsp;
- struct proxy *cur_proxy;
- struct cond_wordlist *wl;
- struct http_res_rule *http_res_last_rule = NULL;
-
- DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
- now_ms, __FUNCTION__,
- t,
- rep,
- rep->rex, rep->wex,
- rep->flags,
- rep->buf->i,
- rep->analysers);
-
- if (unlikely(msg->msg_state < HTTP_MSG_BODY)) /* we need more data */
- return 0;
-
- rep->analysers &= ~an_bit;
- rep->analyse_exp = TICK_ETERNITY;
-
/* Now we have to check if we need to modify the Connection header.
* This is more difficult on the response than it is on the request,
* because we can have two different HTTP versions and we don't know
@@ -5789,14 +6037,14 @@
}
else if ((txn->status >= 200) && !(txn->flags & TX_HDR_CONN_PRS) &&
((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN ||
- ((t->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
- (t->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))) {
+ ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))) {
int to_del = 0;
/* this situation happens when combining pretend-keepalive with httpclose. */
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL &&
- ((t->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
- (t->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))
+ ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))
txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO;
/* on unknown transfer length, we must close */
@@ -5831,287 +6079,314 @@
}
/* we want to have the response time before we start processing it */
- t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
-
- if (1) {
- /*
- * 3: we will have to evaluate the filters.
- * As opposed to version 1.2, now they will be evaluated in the
- * filters order and not in the header order. This means that
- * each filter has to be validated among all headers.
- *
- * Filters are tried with ->be first, then with ->fe if it is
- * different from ->be.
- */
-
- cur_proxy = t->be;
- while (1) {
- struct proxy *rule_set = cur_proxy;
-
- /* evaluate http-response rules */
- if (!http_res_last_rule)
- http_res_last_rule = http_res_get_intercept_rule(cur_proxy, &cur_proxy->http_res_rules, t, txn);
-
- /* try headers filters */
- if (rule_set->rsp_exp != NULL) {
- if (apply_filters_to_response(t, rep, rule_set) < 0) {
- return_bad_resp:
- if (objt_server(t->target)) {
- objt_server(t->target)->counters.failed_resp++;
- health_adjust(objt_server(t->target), HANA_STATUS_HTTP_RSP);
- }
- t->be->be_counters.failed_resp++;
- return_srv_prx_502:
- rep->analysers = 0;
- txn->status = 502;
- t->logs.t_data = -1; /* was not a valid response */
- rep->prod->flags |= SI_FL_NOLINGER;
- bi_erase(rep);
- stream_int_retnclose(rep->cons, http_error_message(t, HTTP_ERR_502));
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_H;
- return 0;
- }
- }
+ s->logs.t_data = tv_ms_elapsed(&s->logs.tv_accept, &now);
- /* has the response been denied ? */
- if (txn->flags & TX_SVDENY) {
- if (objt_server(t->target))
- objt_server(t->target)->counters.failed_secu++;
-
- t->be->be_counters.denied_resp++;
- t->fe->fe_counters.denied_resp++;
- if (t->listener->counters)
- t->listener->counters->denied_resp++;
-
- goto return_srv_prx_502;
- }
-
- /* add response headers from the rule sets in the same order */
- list_for_each_entry(wl, &rule_set->rsp_add, list) {
- if (txn->status < 200)
- break;
- if (wl->cond) {
- int ret = acl_exec_cond(wl->cond, px, t, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL);
- ret = acl_pass(ret);
- if (((struct acl_cond *)wl->cond)->pol == ACL_COND_UNLESS)
- ret = !ret;
- if (!ret)
- continue;
- }
- if (unlikely(http_header_add_tail(&txn->rsp, &txn->hdr_idx, wl->s) < 0))
- goto return_bad_resp;
- }
+ /* end of job, return OK */
+ rep->analysers &= ~an_bit;
+ rep->analyse_exp = TICK_ETERNITY;
+ channel_auto_close(rep);
+ return 1;
- /* check whether we're already working on the frontend */
- if (cur_proxy == t->fe)
- break;
- cur_proxy = t->fe;
- }
+ abort_keep_alive:
+ /* A keep-alive request to the server failed on a network error.
+ * The client is required to retry. We need to close without returning
+ * any other information so that the client retries.
+ */
+ txn->status = 0;
+ rep->analysers = 0;
+ s->req->analysers = 0;
+ channel_auto_close(rep);
+ s->logs.logwait = 0;
+ s->logs.level = 0;
+ s->rep->flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */
+ bi_erase(rep);
+ stream_int_retnclose(rep->cons, NULL);
+ return 0;
+}
- /*
- * We may be facing a 100-continue response, in which case this
- * is not the right response, and we're waiting for the next one.
- * Let's allow this response to go to the client and wait for the
- * next one.
- */
- if (unlikely(txn->status == 100)) {
- hdr_idx_init(&txn->hdr_idx);
- msg->next -= channel_forward(rep, msg->next);
- msg->msg_state = HTTP_MSG_RPBEFORE;
- txn->status = 0;
- t->logs.t_data = -1; /* was not a response yet */
- rep->analysers |= AN_RES_WAIT_HTTP | an_bit;
- return 1;
- }
- else if (unlikely(txn->status < 200))
- goto skip_header_mangling;
+/* This function performs all the processing enabled for the current response.
+ * It normally returns 1 unless it wants to break. It relies on buffers flags,
+ * and updates s->rep->analysers. It might make sense to explode it into several
+ * other functions. It works like process_request (see indications above).
+ */
+int http_process_res_common(struct session *s, struct channel *rep, int an_bit, struct proxy *px)
+{
+ struct http_txn *txn = &s->txn;
+ struct http_msg *msg = &txn->rsp;
+ struct proxy *cur_proxy;
+ struct cond_wordlist *wl;
+ struct http_res_rule *http_res_last_rule = NULL;
- /* we don't have any 1xx status code now */
+ DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
+ now_ms, __FUNCTION__,
+ s,
+ rep,
+ rep->rex, rep->wex,
+ rep->flags,
+ rep->buf->i,
+ rep->analysers);
- /*
- * 4: check for server cookie.
- */
- if (t->be->cookie_name || t->be->appsession_name || t->fe->capture_name ||
- (t->be->options & PR_O_CHK_CACHE))
- manage_server_side_cookies(t, rep);
+ if (unlikely(msg->msg_state < HTTP_MSG_BODY)) /* we need more data */
+ return 0;
+ rep->analysers &= ~an_bit;
+ rep->analyse_exp = TICK_ETERNITY;
- /*
- * 5: check for cache-control or pragma headers if required.
- */
- if ((t->be->options & PR_O_CHK_CACHE) || (t->be->ck_opts & PR_CK_NOC))
- check_response_for_cacheability(t, rep);
+ /* The stats applet needs to adjust the Connection header but we don't
+ * apply any filter there.
+ */
+ if (unlikely(objt_applet(s->target) == &http_stats_applet))
+ goto skip_filters;
- /*
- * 6: add server cookie in the response if needed
- */
- if (objt_server(t->target) && (t->be->ck_opts & PR_CK_INS) &&
- !((txn->flags & TX_SCK_FOUND) && (t->be->ck_opts & PR_CK_PSV)) &&
- (!(t->flags & SN_DIRECT) ||
- ((t->be->cookie_maxidle || txn->cookie_last_date) &&
- (!txn->cookie_last_date || (txn->cookie_last_date - date.tv_sec) < 0)) ||
- (t->be->cookie_maxlife && !txn->cookie_first_date) || // set the first_date
- (!t->be->cookie_maxlife && txn->cookie_first_date)) && // remove the first_date
- (!(t->be->ck_opts & PR_CK_POST) || (txn->meth == HTTP_METH_POST)) &&
- !(t->flags & SN_IGNORE_PRST)) {
- /* the server is known, it's not the one the client requested, or the
- * cookie's last seen date needs to be refreshed. We have to
- * insert a set-cookie here, except if we want to insert only on POST
- * requests and this one isn't. Note that servers which don't have cookies
- * (eg: some backup servers) will return a full cookie removal request.
- */
- if (!objt_server(t->target)->cookie) {
- chunk_printf(&trash,
- "Set-Cookie: %s=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/",
- t->be->cookie_name);
- }
- else {
- chunk_printf(&trash, "Set-Cookie: %s=%s", t->be->cookie_name, objt_server(t->target)->cookie);
+ /*
+ * We will have to evaluate the filters.
+ * As opposed to version 1.2, now they will be evaluated in the
+ * filters order and not in the header order. This means that
+ * each filter has to be validated among all headers.
+ *
+ * Filters are tried with ->be first, then with ->fe if it is
+ * different from ->be.
+ */
- if (t->be->cookie_maxidle || t->be->cookie_maxlife) {
- /* emit last_date, which is mandatory */
- trash.str[trash.len++] = COOKIE_DELIM_DATE;
- s30tob64((date.tv_sec+3) >> 2, trash.str + trash.len);
- trash.len += 5;
+ cur_proxy = s->be;
+ while (1) {
+ struct proxy *rule_set = cur_proxy;
- if (t->be->cookie_maxlife) {
- /* emit first_date, which is either the original one or
- * the current date.
- */
- trash.str[trash.len++] = COOKIE_DELIM_DATE;
- s30tob64(txn->cookie_first_date ?
- txn->cookie_first_date >> 2 :
- (date.tv_sec+3) >> 2, trash.str + trash.len);
- trash.len += 5;
- }
- }
- chunk_appendf(&trash, "; path=/");
+ /* evaluate http-response rules */
+ if (!http_res_last_rule)
+ http_res_last_rule = http_res_get_intercept_rule(cur_proxy, &cur_proxy->http_res_rules, s, txn);
+
+ /* try headers filters */
+ if (rule_set->rsp_exp != NULL) {
+ if (apply_filters_to_response(s, rep, rule_set) < 0) {
+ return_bad_resp:
+ if (objt_server(s->target)) {
+ objt_server(s->target)->counters.failed_resp++;
+ health_adjust(objt_server(s->target), HANA_STATUS_HTTP_RSP);
+ }
+ s->be->be_counters.failed_resp++;
+ return_srv_prx_502:
+ rep->analysers = 0;
+ txn->status = 502;
+ s->logs.t_data = -1; /* was not a valid response */
+ rep->prod->flags |= SI_FL_NOLINGER;
+ bi_erase(rep);
+ stream_int_retnclose(rep->cons, http_error_message(s, HTTP_ERR_502));
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_PRXCOND;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_H;
+ return 0;
}
+ }
- if (t->be->cookie_domain)
- chunk_appendf(&trash, "; domain=%s", t->be->cookie_domain);
+ /* has the response been denied ? */
+ if (txn->flags & TX_SVDENY) {
+ if (objt_server(s->target))
+ objt_server(s->target)->counters.failed_secu++;
- if (t->be->ck_opts & PR_CK_HTTPONLY)
- chunk_appendf(&trash, "; HttpOnly");
+ s->be->be_counters.denied_resp++;
+ s->fe->fe_counters.denied_resp++;
+ if (s->listener->counters)
+ s->listener->counters->denied_resp++;
- if (t->be->ck_opts & PR_CK_SECURE)
- chunk_appendf(&trash, "; Secure");
+ goto return_srv_prx_502;
+ }
- if (unlikely(http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len) < 0))
+ /* add response headers from the rule sets in the same order */
+ list_for_each_entry(wl, &rule_set->rsp_add, list) {
+ if (txn->status < 200)
+ break;
+ if (wl->cond) {
+ int ret = acl_exec_cond(wl->cond, px, s, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL);
+ ret = acl_pass(ret);
+ if (((struct acl_cond *)wl->cond)->pol == ACL_COND_UNLESS)
+ ret = !ret;
+ if (!ret)
+ continue;
+ }
+ if (unlikely(http_header_add_tail(&txn->rsp, &txn->hdr_idx, wl->s) < 0))
goto return_bad_resp;
+ }
- txn->flags &= ~TX_SCK_MASK;
- if (objt_server(t->target)->cookie && (t->flags & SN_DIRECT))
- /* the server did not change, only the date was updated */
- txn->flags |= TX_SCK_UPDATED;
- else
- txn->flags |= TX_SCK_INSERTED;
+ /* check whether we're already working on the frontend */
+ if (cur_proxy == s->fe)
+ break;
+ cur_proxy = s->fe;
+ }
- /* Here, we will tell an eventual cache on the client side that we don't
- * want it to cache this reply because HTTP/1.0 caches also cache cookies !
- * Some caches understand the correct form: 'no-cache="set-cookie"', but
- * others don't (eg: apache <= 1.3.26). So we use 'private' instead.
- */
- if ((t->be->ck_opts & PR_CK_NOC) && (txn->flags & TX_CACHEABLE)) {
+ /* OK that's all we can do for 1xx responses */
+ if (unlikely(txn->status < 200))
+ goto skip_header_mangling;
- txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
+ /*
+ * Now check for a server cookie.
+ */
+ if (s->be->cookie_name || s->be->appsession_name || s->fe->capture_name ||
+ (s->be->options & PR_O_CHK_CACHE))
+ manage_server_side_cookies(s, rep);
- if (unlikely(http_header_add_tail2(&txn->rsp, &txn->hdr_idx,
- "Cache-control: private", 22) < 0))
- goto return_bad_resp;
- }
- }
+ /*
+ * Check for cache-control or pragma headers if required.
+ */
+ if ((s->be->options & PR_O_CHK_CACHE) || (s->be->ck_opts & PR_CK_NOC))
+ check_response_for_cacheability(s, rep);
- /*
- * 7: check if result will be cacheable with a cookie.
- * We'll block the response if security checks have caught
- * nasty things such as a cacheable cookie.
- */
- if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) ==
- (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) &&
- (t->be->options & PR_O_CHK_CACHE)) {
-
- /* we're in presence of a cacheable response containing
- * a set-cookie header. We'll block it as requested by
- * the 'checkcache' option, and send an alert.
- */
- if (objt_server(t->target))
- objt_server(t->target)->counters.failed_secu++;
-
- t->be->be_counters.denied_resp++;
- t->fe->fe_counters.denied_resp++;
- if (t->listener->counters)
- t->listener->counters->denied_resp++;
-
- Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
- t->be->id, objt_server(t->target) ? objt_server(t->target)->id : "<dispatch>");
- send_log(t->be, LOG_ALERT,
- "Blocking cacheable cookie in response from instance %s, server %s.\n",
- t->be->id, objt_server(t->target) ? objt_server(t->target)->id : "<dispatch>");
- goto return_srv_prx_502;
+ /*
+ * Add server cookie in the response if needed
+ */
+ if (objt_server(s->target) && (s->be->ck_opts & PR_CK_INS) &&
+ !((txn->flags & TX_SCK_FOUND) && (s->be->ck_opts & PR_CK_PSV)) &&
+ (!(s->flags & SN_DIRECT) ||
+ ((s->be->cookie_maxidle || txn->cookie_last_date) &&
+ (!txn->cookie_last_date || (txn->cookie_last_date - date.tv_sec) < 0)) ||
+ (s->be->cookie_maxlife && !txn->cookie_first_date) || // set the first_date
+ (!s->be->cookie_maxlife && txn->cookie_first_date)) && // remove the first_date
+ (!(s->be->ck_opts & PR_CK_POST) || (txn->meth == HTTP_METH_POST)) &&
+ !(s->flags & SN_IGNORE_PRST)) {
+ /* the server is known, it's not the one the client requested, or the
+ * cookie's last seen date needs to be refreshed. We have to
+ * insert a set-cookie here, except if we want to insert only on POST
+ * requests and this one isn't. Note that servers which don't have cookies
+ * (eg: some backup servers) will return a full cookie removal request.
+ */
+ if (!objt_server(s->target)->cookie) {
+ chunk_printf(&trash,
+ "Set-Cookie: %s=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/",
+ s->be->cookie_name);
}
+ else {
+ chunk_printf(&trash, "Set-Cookie: %s=%s", s->be->cookie_name, objt_server(s->target)->cookie);
- /*
- * 8: adjust "Connection: close" or "Connection: keep-alive" if needed.
- * If an "Upgrade" token is found, the header is left untouched in order
- * not to have to deal with some client bugs : some of them fail an upgrade
- * if anything but "Upgrade" is present in the Connection header.
- */
- if (!(txn->flags & TX_HDR_CONN_UPG) &&
- (((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) ||
- ((t->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
- (t->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))) {
- unsigned int want_flags = 0;
-
- if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
- /* we want a keep-alive response here. Keep-alive header
- * required if either side is not 1.1.
- */
- if (!(txn->req.flags & msg->flags & HTTP_MSGF_VER_11))
- want_flags |= TX_CON_KAL_SET;
- }
- else {
- /* we want a close response here. Close header required if
- * the server is 1.1, regardless of the client.
- */
- if (msg->flags & HTTP_MSGF_VER_11)
- want_flags |= TX_CON_CLO_SET;
+ if (s->be->cookie_maxidle || s->be->cookie_maxlife) {
+ /* emit last_date, which is mandatory */
+ trash.str[trash.len++] = COOKIE_DELIM_DATE;
+ s30tob64((date.tv_sec+3) >> 2, trash.str + trash.len);
+ trash.len += 5;
+
+ if (s->be->cookie_maxlife) {
+ /* emit first_date, which is either the original one or
+ * the current date.
+ */
+ trash.str[trash.len++] = COOKIE_DELIM_DATE;
+ s30tob64(txn->cookie_first_date ?
+ txn->cookie_first_date >> 2 :
+ (date.tv_sec+3) >> 2, trash.str + trash.len);
+ trash.len += 5;
+ }
}
-
- if (want_flags != (txn->flags & (TX_CON_CLO_SET|TX_CON_KAL_SET)))
- http_change_connection_header(txn, msg, want_flags);
+ chunk_appendf(&trash, "; path=/");
}
- skip_header_mangling:
- if ((msg->flags & HTTP_MSGF_XFER_LEN) ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN)
- rep->analysers |= AN_RES_HTTP_XFER_BODY;
+ if (s->be->cookie_domain)
+ chunk_appendf(&trash, "; domain=%s", s->be->cookie_domain);
+
+ if (s->be->ck_opts & PR_CK_HTTPONLY)
+ chunk_appendf(&trash, "; HttpOnly");
+
+ if (s->be->ck_opts & PR_CK_SECURE)
+ chunk_appendf(&trash, "; Secure");
- /*************************************************************
- * OK, that's finished for the headers. We have done what we *
- * could. Let's switch to the DATA state. *
- ************************************************************/
+ if (unlikely(http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len) < 0))
+ goto return_bad_resp;
- /* if the user wants to log as soon as possible, without counting
- * bytes from the server, then this is the right moment. We have
- * to temporarily assign bytes_out to log what we currently have.
+ txn->flags &= ~TX_SCK_MASK;
+ if (objt_server(s->target)->cookie && (s->flags & SN_DIRECT))
+ /* the server did not change, only the date was updated */
+ txn->flags |= TX_SCK_UPDATED;
+ else
+ txn->flags |= TX_SCK_INSERTED;
+
+ /* Here, we will tell an eventual cache on the client side that we don't
+ * want it to cache this reply because HTTP/1.0 caches also cache cookies !
+ * Some caches understand the correct form: 'no-cache="set-cookie"', but
+ * others don't (eg: apache <= 1.3.26). So we use 'private' instead.
*/
- if (!LIST_ISEMPTY(&t->fe->logformat) && !(t->logs.logwait & LW_BYTES)) {
- t->logs.t_close = t->logs.t_data; /* to get a valid end date */
- t->logs.bytes_out = txn->rsp.eoh;
- t->do_log(t);
- t->logs.bytes_out = 0;
+ if ((s->be->ck_opts & PR_CK_NOC) && (txn->flags & TX_CACHEABLE)) {
+
+ txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
+
+ if (unlikely(http_header_add_tail2(&txn->rsp, &txn->hdr_idx,
+ "Cache-control: private", 22) < 0))
+ goto return_bad_resp;
}
+ }
- /* Note: we must not try to cheat by jumping directly to DATA,
- * otherwise we would not let the client side wake up.
+ /*
+ * Check if result will be cacheable with a cookie.
+ * We'll block the response if security checks have caught
+ * nasty things such as a cacheable cookie.
+ */
+ if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) ==
+ (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) &&
+ (s->be->options & PR_O_CHK_CACHE)) {
+ /* we're in presence of a cacheable response containing
+ * a set-cookie header. We'll block it as requested by
+ * the 'checkcache' option, and send an alert.
*/
+ if (objt_server(s->target))
+ objt_server(s->target)->counters.failed_secu++;
- return 1;
+ s->be->be_counters.denied_resp++;
+ s->fe->fe_counters.denied_resp++;
+ if (s->listener->counters)
+ s->listener->counters->denied_resp++;
+
+ Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
+ s->be->id, objt_server(s->target) ? objt_server(s->target)->id : "<dispatch>");
+ send_log(s->be, LOG_ALERT,
+ "Blocking cacheable cookie in response from instance %s, server %s.\n",
+ s->be->id, objt_server(s->target) ? objt_server(s->target)->id : "<dispatch>");
+ goto return_srv_prx_502;
+ }
+
+ skip_filters:
+ /*
+ * Adjust "Connection: close" or "Connection: keep-alive" if needed.
+ * If an "Upgrade" token is found, the header is left untouched in order
+ * not to have to deal with some client bugs : some of them fail an upgrade
+ * if anything but "Upgrade" is present in the Connection header.
+ */
+ if (!(txn->flags & TX_HDR_CONN_UPG) &&
+ (((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) ||
+ ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL ||
+ (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))) {
+ unsigned int want_flags = 0;
+
+ if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
+ /* we want a keep-alive response here. Keep-alive header
+ * required if either side is not 1.1.
+ */
+ if (!(txn->req.flags & msg->flags & HTTP_MSGF_VER_11))
+ want_flags |= TX_CON_KAL_SET;
+ }
+ else {
+ /* we want a close response here. Close header required if
+ * the server is 1.1, regardless of the client.
+ */
+ if (msg->flags & HTTP_MSGF_VER_11)
+ want_flags |= TX_CON_CLO_SET;
+ }
+
+ if (want_flags != (txn->flags & (TX_CON_CLO_SET|TX_CON_KAL_SET)))
+ http_change_connection_header(txn, msg, want_flags);
+ }
+
+ skip_header_mangling:
+ if ((msg->flags & HTTP_MSGF_XFER_LEN) ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN)
+ rep->analysers |= AN_RES_HTTP_XFER_BODY;
+
+ /* if the user wants to log as soon as possible, without counting
+ * bytes from the server, then this is the right moment. We have
+ * to temporarily assign bytes_out to log what we currently have.
+ */
+ if (!LIST_ISEMPTY(&s->fe->logformat) && !(s->logs.logwait & LW_BYTES)) {
+ s->logs.t_close = s->logs.t_data; /* to get a valid end date */
+ s->logs.bytes_out = txn->rsp.eoh;
+ s->do_log(s);
+ s->logs.bytes_out = 0;
}
return 1;
}
@@ -6192,6 +6467,12 @@
}
}
+ if (res->to_forward) {
+ /* We can't process the buffer's contents yet */
+ res->flags |= CF_WAKE_WRITE;
+ goto missing_data;
+ }
+
if (unlikely(s->comp_algo != NULL) && msg->msg_state < HTTP_MSG_TRAILERS) {
/* We need a compression buffer in the DATA state to put the
* output of compressed data, and in CRLF state to let the
@@ -6331,6 +6612,12 @@
}
return 1;
}
+
+ /* if we received everything, we don't want to expire anymore */
+ if (msg->msg_state == HTTP_MSG_DONE) {
+ res->flags |= CF_READ_NOEXP;
+ res->rex = TICK_ETERNITY;
+ }
return 0;
}
}
@@ -6357,7 +6644,7 @@
* server abort.
*/
if (res->flags & CF_SHUTR) {
- if ((res->flags & CF_SHUTW_NOW) || (s->req->flags & CF_SHUTR))
+ if ((s->req->flags & (CF_SHUTR|CF_SHUTW)) == (CF_SHUTR|CF_SHUTW))
goto aborted_xfer;
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_SRVCL;
@@ -6455,12 +6742,12 @@
* Since it can manage the switch to another backend, it updates the per-proxy
* DENY stats.
*/
-int apply_filter_to_req_headers(struct session *t, struct channel *req, struct hdr_exp *exp)
+int apply_filter_to_req_headers(struct session *s, struct channel *req, struct hdr_exp *exp)
{
char term;
char *cur_ptr, *cur_end, *cur_next;
int cur_idx, old_idx, last_hdr;
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
struct hdr_idx_elem *cur_hdr;
int delta;
@@ -6506,11 +6793,11 @@
* FIXME: should we return an HTTP/500 here so that
* the admin knows there's a problem ?
*/
- if (t->be != t->fe)
+ if (s->be != s->fe)
break;
/* Swithing Proxy */
- session_set_backend(t, (struct proxy *)exp->replace);
+ session_set_backend(s, (struct proxy *)exp->replace);
last_hdr = 1;
break;
@@ -6575,12 +6862,12 @@
* Since it can manage the switch to another backend, it updates the per-proxy
* DENY stats.
*/
-int apply_filter_to_req_line(struct session *t, struct channel *req, struct hdr_exp *exp)
+int apply_filter_to_req_line(struct session *s, struct channel *req, struct hdr_exp *exp)
{
char term;
char *cur_ptr, *cur_end;
int done;
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
int delta;
if (unlikely(txn->flags & (TX_CLDENY | TX_CLTARPIT)))
@@ -6615,11 +6902,11 @@
* FIXME: should we return an HTTP/500 here so that
* the admin knows there's a problem ?
*/
- if (t->be != t->fe)
+ if (s->be != s->fe)
break;
/* Swithing Proxy */
- session_set_backend(t, (struct proxy *)exp->replace);
+ session_set_backend(s, (struct proxy *)exp->replace);
done = 1;
break;
@@ -6733,16 +7020,16 @@
* Try to retrieve the server associated to the appsession.
* If the server is found, it's assigned to the session.
*/
-void manage_client_side_appsession(struct session *t, const char *buf, int len) {
- struct http_txn *txn = &t->txn;
+void manage_client_side_appsession(struct session *s, const char *buf, int len) {
+ struct http_txn *txn = &s->txn;
appsess *asession = NULL;
char *sessid_temp = NULL;
- if (len > t->be->appsession_len) {
- len = t->be->appsession_len;
+ if (len > s->be->appsession_len) {
+ len = s->be->appsession_len;
}
- if (t->be->options2 & PR_O2_AS_REQL) {
+ if (s->be->options2 & PR_O2_AS_REQL) {
/* request-learn option is enabled : store the sessid in the session for future use */
if (txn->sessid != NULL) {
/* free previously allocated memory as we don't need the session id found in the URL anymore */
@@ -6751,7 +7038,7 @@
if ((txn->sessid = pool_alloc2(apools.sessid)) == NULL) {
Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
+ send_log(s->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
return;
}
@@ -6761,35 +7048,35 @@
if ((sessid_temp = pool_alloc2(apools.sessid)) == NULL) {
Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
+ send_log(s->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
return;
}
memcpy(sessid_temp, buf, len);
sessid_temp[len] = 0;
- asession = appsession_hash_lookup(&(t->be->htbl_proxy), sessid_temp);
+ asession = appsession_hash_lookup(&(s->be->htbl_proxy), sessid_temp);
/* free previously allocated memory */
pool_free2(apools.sessid, sessid_temp);
if (asession != NULL) {
- asession->expire = tick_add_ifset(now_ms, t->be->timeout.appsession);
- if (!(t->be->options2 & PR_O2_AS_REQL))
+ asession->expire = tick_add_ifset(now_ms, s->be->timeout.appsession);
+ if (!(s->be->options2 & PR_O2_AS_REQL))
asession->request_count++;
if (asession->serverid != NULL) {
- struct server *srv = t->be->srv;
+ struct server *srv = s->be->srv;
while (srv) {
if (strcmp(srv->id, asession->serverid) == 0) {
if ((srv->state & SRV_RUNNING) ||
- (t->be->options & PR_O_PERSIST) ||
- (t->flags & SN_FORCE_PRST)) {
+ (s->be->options & PR_O_PERSIST) ||
+ (s->flags & SN_FORCE_PRST)) {
/* we found the server and it's usable */
txn->flags &= ~TX_CK_MASK;
txn->flags |= (srv->state & SRV_RUNNING) ? TX_CK_VALID : TX_CK_DOWN;
- t->flags |= SN_DIRECT | SN_ASSIGNED;
- t->target = &srv->obj_type;
+ s->flags |= SN_DIRECT | SN_ASSIGNED;
+ s->target = &srv->obj_type;
break;
} else {
@@ -6884,9 +7171,9 @@
* of the multiple very crappy and ambiguous syntaxes we have to support. it
* highly recommended not to touch this part without a good reason !
*/
-void manage_client_side_cookies(struct session *t, struct channel *req)
+void manage_client_side_cookies(struct session *s, struct channel *req)
{
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
int preserve_hdr;
int cur_idx, old_idx;
char *hdr_beg, *hdr_end, *hdr_next, *del_from;
@@ -7070,16 +7357,16 @@
* can only capture one. Also as an optimisation, we ignore
* cookies shorter than the declared name.
*/
- if (t->fe->capture_name != NULL && txn->cli_cookie == NULL &&
- (val_end - att_beg >= t->fe->capture_namelen) &&
- memcmp(att_beg, t->fe->capture_name, t->fe->capture_namelen) == 0) {
+ if (s->fe->capture_name != NULL && txn->cli_cookie == NULL &&
+ (val_end - att_beg >= s->fe->capture_namelen) &&
+ memcmp(att_beg, s->fe->capture_name, s->fe->capture_namelen) == 0) {
int log_len = val_end - att_beg;
if ((txn->cli_cookie = pool_alloc2(pool2_capture)) == NULL) {
Alert("HTTP logging : out of memory.\n");
} else {
- if (log_len > t->fe->capture_len)
- log_len = t->fe->capture_len;
+ if (log_len > s->fe->capture_len)
+ log_len = s->fe->capture_len;
memcpy(txn->cli_cookie, att_beg, log_len);
txn->cli_cookie[log_len] = 0;
}
@@ -7094,9 +7381,9 @@
*
* Cookie: NAME=SRV~VALUE
*/
- if ((att_end - att_beg == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
- (memcmp(att_beg, t->be->cookie_name, att_end - att_beg) == 0)) {
- struct server *srv = t->be->srv;
+ if ((att_end - att_beg == s->be->cookie_len) && (s->be->cookie_name != NULL) &&
+ (memcmp(att_beg, s->be->cookie_name, att_end - att_beg) == 0)) {
+ struct server *srv = s->be->srv;
char *delim;
/* if we're in cookie prefix mode, we'll search the delimitor so that we
@@ -7115,7 +7402,7 @@
* +-------------------------> hdr_beg
*/
- if (t->be->ck_opts & PR_CK_PFX) {
+ if (s->be->ck_opts & PR_CK_PFX) {
for (delim = val_beg; delim < val_end; delim++)
if (*delim == COOKIE_DELIM)
break;
@@ -7159,8 +7446,8 @@
* and at the same time avoids keeping unwanted side effects for too
* long.
*/
- if (txn->cookie_first_date && t->be->cookie_maxlife &&
- (((signed)(date.tv_sec - txn->cookie_first_date) > (signed)t->be->cookie_maxlife) ||
+ if (txn->cookie_first_date && s->be->cookie_maxlife &&
+ (((signed)(date.tv_sec - txn->cookie_first_date) > (signed)s->be->cookie_maxlife) ||
((signed)(txn->cookie_first_date - date.tv_sec) > 86400))) {
txn->flags &= ~TX_CK_MASK;
txn->flags |= TX_CK_OLD;
@@ -7168,8 +7455,8 @@
txn->cookie_first_date = 0;
txn->cookie_last_date = 0;
}
- else if (txn->cookie_last_date && t->be->cookie_maxidle &&
- (((signed)(date.tv_sec - txn->cookie_last_date) > (signed)t->be->cookie_maxidle) ||
+ else if (txn->cookie_last_date && s->be->cookie_maxidle &&
+ (((signed)(date.tv_sec - txn->cookie_last_date) > (signed)s->be->cookie_maxidle) ||
((signed)(txn->cookie_last_date - date.tv_sec) > 86400))) {
txn->flags &= ~TX_CK_MASK;
txn->flags |= TX_CK_EXPIRED;
@@ -7186,20 +7473,20 @@
* empty cookies and mark them as invalid.
* The same behaviour is applied when persistence must be ignored.
*/
- if ((delim == val_beg) || (t->flags & (SN_IGNORE_PRST | SN_ASSIGNED)))
+ if ((delim == val_beg) || (s->flags & (SN_IGNORE_PRST | SN_ASSIGNED)))
srv = NULL;
while (srv) {
if (srv->cookie && (srv->cklen == delim - val_beg) &&
!memcmp(val_beg, srv->cookie, delim - val_beg)) {
if ((srv->state & SRV_RUNNING) ||
- (t->be->options & PR_O_PERSIST) ||
- (t->flags & SN_FORCE_PRST)) {
+ (s->be->options & PR_O_PERSIST) ||
+ (s->flags & SN_FORCE_PRST)) {
/* we found the server and we can use it */
txn->flags &= ~TX_CK_MASK;
txn->flags |= (srv->state & SRV_RUNNING) ? TX_CK_VALID : TX_CK_DOWN;
- t->flags |= SN_DIRECT | SN_ASSIGNED;
- t->target = &srv->obj_type;
+ s->flags |= SN_DIRECT | SN_ASSIGNED;
+ s->target = &srv->obj_type;
break;
} else {
/* we found a server, but it's down,
@@ -7216,7 +7503,7 @@
if (!srv && !(txn->flags & (TX_CK_DOWN|TX_CK_EXPIRED|TX_CK_OLD))) {
/* no server matched this cookie or we deliberately skipped it */
txn->flags &= ~TX_CK_MASK;
- if ((t->flags & (SN_IGNORE_PRST | SN_ASSIGNED)))
+ if ((s->flags & (SN_IGNORE_PRST | SN_ASSIGNED)))
txn->flags |= TX_CK_UNUSED;
else
txn->flags |= TX_CK_INVALID;
@@ -7229,7 +7516,7 @@
* application cookie so that it does not get accidentely removed later,
* if we're in cookie prefix mode
*/
- if ((t->be->ck_opts & PR_CK_PFX) && (delim != val_end)) {
+ if ((s->be->ck_opts & PR_CK_PFX) && (delim != val_end)) {
int delta; /* negative */
delta = buffer_replace2(req->buf, val_beg, delim + 1, NULL, 0);
@@ -7244,7 +7531,7 @@
preserve_hdr = 1; /* we want to keep this cookie */
}
else if (del_from == NULL &&
- (t->be->ck_opts & (PR_CK_INS | PR_CK_IND)) == (PR_CK_INS | PR_CK_IND)) {
+ (s->be->ck_opts & (PR_CK_INS | PR_CK_IND)) == (PR_CK_INS | PR_CK_IND)) {
del_from = prev;
}
} else {
@@ -7273,14 +7560,14 @@
}
/* Look for the appsession cookie unless persistence must be ignored */
- if (!(t->flags & SN_IGNORE_PRST) && (t->be->appsession_name != NULL)) {
+ if (!(s->flags & SN_IGNORE_PRST) && (s->be->appsession_name != NULL)) {
int cmp_len, value_len;
char *value_begin;
- if (t->be->options2 & PR_O2_AS_PFX) {
- cmp_len = MIN(val_end - att_beg, t->be->appsession_name_len);
- value_begin = att_beg + t->be->appsession_name_len;
- value_len = val_end - att_beg - t->be->appsession_name_len;
+ if (s->be->options2 & PR_O2_AS_PFX) {
+ cmp_len = MIN(val_end - att_beg, s->be->appsession_name_len);
+ value_begin = att_beg + s->be->appsession_name_len;
+ value_len = val_end - att_beg - s->be->appsession_name_len;
} else {
cmp_len = att_end - att_beg;
value_begin = val_beg;
@@ -7288,9 +7575,9 @@
}
/* let's see if the cookie is our appcookie */
- if (cmp_len == t->be->appsession_name_len &&
- memcmp(att_beg, t->be->appsession_name, cmp_len) == 0) {
- manage_client_side_appsession(t, value_begin, value_len);
+ if (cmp_len == s->be->appsession_name_len &&
+ memcmp(att_beg, s->be->appsession_name, cmp_len) == 0) {
+ manage_client_side_appsession(s, value_begin, value_len);
}
}
@@ -7333,12 +7620,12 @@
/* Iterate the same filter through all response headers contained in <rtr>.
* Returns 1 if this filter can be stopped upon return, otherwise 0.
*/
-int apply_filter_to_resp_headers(struct session *t, struct channel *rtr, struct hdr_exp *exp)
+int apply_filter_to_resp_headers(struct session *s, struct channel *rtr, struct hdr_exp *exp)
{
char term;
char *cur_ptr, *cur_end, *cur_next;
int cur_idx, old_idx, last_hdr;
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
struct hdr_idx_elem *cur_hdr;
int delta;
@@ -7432,12 +7719,12 @@
* Returns 0 if nothing has been done, 1 if the filter has been applied,
* or -1 if a replacement resulted in an invalid status line.
*/
-int apply_filter_to_sts_line(struct session *t, struct channel *rtr, struct hdr_exp *exp)
+int apply_filter_to_sts_line(struct session *s, struct channel *rtr, struct hdr_exp *exp)
{
char term;
char *cur_ptr, *cur_end;
int done;
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
int delta;
@@ -7572,9 +7859,9 @@
* desirable to call it only when needed. This function is also used when we
* just need to know if there is a cookie (eg: for check-cache).
*/
-void manage_server_side_cookies(struct session *t, struct channel *res)
+void manage_server_side_cookies(struct session *s, struct channel *res)
{
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
struct server *srv;
int is_cookie2;
int cur_idx, old_idx, delta;
@@ -7623,9 +7910,9 @@
* check-cache is enabled) and we are not interested in checking
* them. Warning, the cookie capture is declared in the frontend.
*/
- if (t->be->cookie_name == NULL &&
- t->be->appsession_name == NULL &&
- t->fe->capture_name == NULL)
+ if (s->be->cookie_name == NULL &&
+ s->be->appsession_name == NULL &&
+ s->fe->capture_name == NULL)
return;
/* OK so now we know we have to process this response cookie.
@@ -7759,27 +8046,27 @@
* can only capture one. Also as an optimisation, we ignore
* cookies shorter than the declared name.
*/
- if (t->fe->capture_name != NULL &&
+ if (s->fe->capture_name != NULL &&
txn->srv_cookie == NULL &&
- (val_end - att_beg >= t->fe->capture_namelen) &&
- memcmp(att_beg, t->fe->capture_name, t->fe->capture_namelen) == 0) {
+ (val_end - att_beg >= s->fe->capture_namelen) &&
+ memcmp(att_beg, s->fe->capture_name, s->fe->capture_namelen) == 0) {
int log_len = val_end - att_beg;
if ((txn->srv_cookie = pool_alloc2(pool2_capture)) == NULL) {
Alert("HTTP logging : out of memory.\n");
}
else {
- if (log_len > t->fe->capture_len)
- log_len = t->fe->capture_len;
+ if (log_len > s->fe->capture_len)
+ log_len = s->fe->capture_len;
memcpy(txn->srv_cookie, att_beg, log_len);
txn->srv_cookie[log_len] = 0;
}
}
- srv = objt_server(t->target);
+ srv = objt_server(s->target);
/* now check if we need to process it for persistence */
- if (!(t->flags & SN_IGNORE_PRST) &&
- (att_end - att_beg == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
- (memcmp(att_beg, t->be->cookie_name, att_end - att_beg) == 0)) {
+ if (!(s->flags & SN_IGNORE_PRST) &&
+ (att_end - att_beg == s->be->cookie_len) && (s->be->cookie_name != NULL) &&
+ (memcmp(att_beg, s->be->cookie_name, att_end - att_beg) == 0)) {
/* assume passive cookie by default */
txn->flags &= ~TX_SCK_MASK;
txn->flags |= TX_SCK_FOUND;
@@ -7789,13 +8076,13 @@
* We'll delete it too if the "indirect" option is set and we're in
* a direct access.
*/
- if (t->be->ck_opts & PR_CK_PSV) {
+ if (s->be->ck_opts & PR_CK_PSV) {
/* The "preserve" flag was set, we don't want to touch the
* server's cookie.
*/
}
- else if ((srv && (t->be->ck_opts & PR_CK_INS)) ||
- ((t->flags & SN_DIRECT) && (t->be->ck_opts & PR_CK_IND))) {
+ else if ((srv && (s->be->ck_opts & PR_CK_INS)) ||
+ ((s->flags & SN_DIRECT) && (s->be->ck_opts & PR_CK_IND))) {
/* this cookie must be deleted */
if (*prev == ':' && next == hdr_end) {
/* whole header */
@@ -7822,7 +8109,7 @@
txn->flags |= TX_SCK_DELETED;
/* and go on with next cookie */
}
- else if (srv && srv->cookie && (t->be->ck_opts & PR_CK_RW)) {
+ else if (srv && srv->cookie && (s->be->ck_opts & PR_CK_RW)) {
/* replace bytes val_beg->val_end with the cookie name associated
* with this server since we know it.
*/
@@ -7836,7 +8123,7 @@
txn->flags &= ~TX_SCK_MASK;
txn->flags |= TX_SCK_REPLACED;
}
- else if (srv && srv->cookie && (t->be->ck_opts & PR_CK_PFX)) {
+ else if (srv && srv->cookie && (s->be->ck_opts & PR_CK_PFX)) {
/* insert the cookie name associated with this server
* before existing cookie, and insert a delimiter between them..
*/
@@ -7853,29 +8140,29 @@
}
}
/* next, let's see if the cookie is our appcookie, unless persistence must be ignored */
- else if (!(t->flags & SN_IGNORE_PRST) && (t->be->appsession_name != NULL)) {
+ else if (!(s->flags & SN_IGNORE_PRST) && (s->be->appsession_name != NULL)) {
int cmp_len, value_len;
char *value_begin;
- if (t->be->options2 & PR_O2_AS_PFX) {
- cmp_len = MIN(val_end - att_beg, t->be->appsession_name_len);
- value_begin = att_beg + t->be->appsession_name_len;
- value_len = MIN(t->be->appsession_len, val_end - att_beg - t->be->appsession_name_len);
+ if (s->be->options2 & PR_O2_AS_PFX) {
+ cmp_len = MIN(val_end - att_beg, s->be->appsession_name_len);
+ value_begin = att_beg + s->be->appsession_name_len;
+ value_len = MIN(s->be->appsession_len, val_end - att_beg - s->be->appsession_name_len);
} else {
cmp_len = att_end - att_beg;
value_begin = val_beg;
- value_len = MIN(t->be->appsession_len, val_end - val_beg);
+ value_len = MIN(s->be->appsession_len, val_end - val_beg);
}
- if ((cmp_len == t->be->appsession_name_len) &&
- (memcmp(att_beg, t->be->appsession_name, t->be->appsession_name_len) == 0)) {
+ if ((cmp_len == s->be->appsession_name_len) &&
+ (memcmp(att_beg, s->be->appsession_name, s->be->appsession_name_len) == 0)) {
/* free a possibly previously allocated memory */
pool_free2(apools.sessid, txn->sessid);
/* Store the sessid in the session for future use */
if ((txn->sessid = pool_alloc2(apools.sessid)) == NULL) {
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
+ send_log(s->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
return;
}
memcpy(txn->sessid, value_begin, value_len);
@@ -7893,51 +8180,51 @@
if (txn->sessid != NULL) {
appsess *asession = NULL;
/* only do insert, if lookup fails */
- asession = appsession_hash_lookup(&(t->be->htbl_proxy), txn->sessid);
+ asession = appsession_hash_lookup(&(s->be->htbl_proxy), txn->sessid);
if (asession == NULL) {
size_t server_id_len;
if ((asession = pool_alloc2(pool2_appsess)) == NULL) {
Alert("Not enough Memory process_srv():asession:calloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession:calloc().\n");
+ send_log(s->be, LOG_ALERT, "Not enough Memory process_srv():asession:calloc().\n");
return;
}
asession->serverid = NULL; /* to avoid a double free in case of allocation error */
if ((asession->sessid = pool_alloc2(apools.sessid)) == NULL) {
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
- t->be->htbl_proxy.destroy(asession);
+ send_log(s->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
+ s->be->htbl_proxy.destroy(asession);
return;
}
- memcpy(asession->sessid, txn->sessid, t->be->appsession_len);
- asession->sessid[t->be->appsession_len] = 0;
+ memcpy(asession->sessid, txn->sessid, s->be->appsession_len);
+ asession->sessid[s->be->appsession_len] = 0;
- server_id_len = strlen(objt_server(t->target)->id) + 1;
+ server_id_len = strlen(objt_server(s->target)->id) + 1;
if ((asession->serverid = pool_alloc2(apools.serverid)) == NULL) {
Alert("Not enough Memory process_srv():asession->serverid:malloc().\n");
- send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
- t->be->htbl_proxy.destroy(asession);
+ send_log(s->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
+ s->be->htbl_proxy.destroy(asession);
return;
}
asession->serverid[0] = '\0';
- memcpy(asession->serverid, objt_server(t->target)->id, server_id_len);
+ memcpy(asession->serverid, objt_server(s->target)->id, server_id_len);
asession->request_count = 0;
- appsession_hash_insert(&(t->be->htbl_proxy), asession);
+ appsession_hash_insert(&(s->be->htbl_proxy), asession);
}
- asession->expire = tick_add_ifset(now_ms, t->be->timeout.appsession);
+ asession->expire = tick_add_ifset(now_ms, s->be->timeout.appsession);
asession->request_count++;
}
}
/*
- * Check if response is cacheable or not. Updates t->flags.
+ * Check if response is cacheable or not. Updates s->flags.
*/
-void check_response_for_cacheability(struct session *t, struct channel *rtr)
+void check_response_for_cacheability(struct session *s, struct channel *rtr)
{
- struct http_txn *txn = &t->txn;
+ struct http_txn *txn = &s->txn;
char *p1, *p2;
char *cur_ptr, *cur_end, *cur_next;
@@ -8024,16 +8311,16 @@
* Try to retrieve a known appsession in the URI, then the associated server.
* If the server is found, it's assigned to the session.
*/
-void get_srv_from_appsession(struct session *t, const char *begin, int len)
+void get_srv_from_appsession(struct session *s, const char *begin, int len)
{
char *end_params, *first_param, *cur_param, *next_param;
char separator;
int value_len;
- int mode = t->be->options2 & PR_O2_AS_M_ANY;
+ int mode = s->be->options2 & PR_O2_AS_M_ANY;
- if (t->be->appsession_name == NULL ||
- (t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST && t->txn.meth != HTTP_METH_HEAD)) {
+ if (s->be->appsession_name == NULL ||
+ (s->txn.meth != HTTP_METH_GET && s->txn.meth != HTTP_METH_POST && s->txn.meth != HTTP_METH_HEAD)) {
return;
}
@@ -8072,14 +8359,14 @@
cur_param--;
if ((cur_param[0] == separator) || (cur_param == first_param)) {
/* let's see if this is the appsession parameter */
- if ((cur_param + t->be->appsession_name_len + 1 < next_param) &&
- ((t->be->options2 & PR_O2_AS_PFX) || cur_param[t->be->appsession_name_len + 1] == '=') &&
- (strncasecmp(cur_param + 1, t->be->appsession_name, t->be->appsession_name_len) == 0)) {
+ if ((cur_param + s->be->appsession_name_len + 1 < next_param) &&
+ ((s->be->options2 & PR_O2_AS_PFX) || cur_param[s->be->appsession_name_len + 1] == '=') &&
+ (strncasecmp(cur_param + 1, s->be->appsession_name, s->be->appsession_name_len) == 0)) {
/* Cool... it's the right one */
- cur_param += t->be->appsession_name_len + (t->be->options2 & PR_O2_AS_PFX ? 1 : 2);
- value_len = MIN(t->be->appsession_len, next_param - cur_param);
+ cur_param += s->be->appsession_name_len + (s->be->options2 & PR_O2_AS_PFX ? 1 : 2);
+ value_len = MIN(s->be->appsession_len, next_param - cur_param);
if (value_len > 0) {
- manage_client_side_appsession(t, cur_param, value_len);
+ manage_client_side_appsession(s, cur_param, value_len);
}
break;
}
@@ -8088,7 +8375,7 @@
}
#if defined(DEBUG_HASH)
Alert("get_srv_from_appsession\n");
- appsession_hash_dump(&(t->be->htbl_proxy));
+ appsession_hash_dump(&(s->be->htbl_proxy));
#endif
}
@@ -8308,13 +8595,13 @@
* so it is safe to pass it a full buffer if needed. If <err> is not NULL, an
* arrow is printed after the line which contains the pointer.
*/
-void debug_hdr(const char *dir, struct session *t, const char *start, const char *end)
+void debug_hdr(const char *dir, struct session *s, const char *start, const char *end)
{
int max;
- chunk_printf(&trash, "%08x:%s.%s[%04x:%04x]: ", t->uniq_id, t->be->id,
+ chunk_printf(&trash, "%08x:%s.%s[%04x:%04x]: ", s->uniq_id, s->be->id,
dir,
- objt_conn(t->req->prod->end) ? (unsigned short)objt_conn(t->req->prod->end)->t.sock.fd : -1,
- objt_conn(t->req->cons->end) ? (unsigned short)objt_conn(t->req->cons->end)->t.sock.fd : -1);
+ objt_conn(s->req->prod->end) ? (unsigned short)objt_conn(s->req->prod->end)->t.sock.fd : -1,
+ objt_conn(s->req->cons->end) ? (unsigned short)objt_conn(s->req->cons->end)->t.sock.fd : -1);
for (max = 0; start + max < end; max++)
if (start[max] == '\r' || start[max] == '\n')
@@ -8473,6 +8760,7 @@
struct http_req_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
{
struct http_req_rule *rule;
+ struct http_req_action_kw *custom = NULL;
int cur_arg;
rule = (struct http_req_rule*)calloc(1, sizeof(struct http_req_rule));
@@ -8484,7 +8772,7 @@
if (!strcmp(args[0], "allow")) {
rule->action = HTTP_REQ_ACT_ALLOW;
cur_arg = 1;
- } else if (!strcmp(args[0], "deny")) {
+ } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block")) {
rule->action = HTTP_REQ_ACT_DENY;
cur_arg = 1;
} else if (!strcmp(args[0], "tarpit")) {
@@ -8644,8 +8932,135 @@
redir->cond = NULL;
cur_arg = 2;
return rule;
+ } else if (strncmp(args[0], "add-acl", 7) == 0) {
+ /* http-request add-acl(<reference (acl name)>) <key pattern> */
+ rule->action = HTTP_REQ_ACT_ADD_ACL;
+ /*
+ * '+ 8' for 'add-acl('
+ * '- 9' for 'add-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRQ;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+ cur_arg += 1;
+ } else if (strncmp(args[0], "del-acl", 7) == 0) {
+ /* http-request del-acl(<reference (acl name)>) <key pattern> */
+ rule->action = HTTP_REQ_ACT_DEL_ACL;
+ /*
+ * '+ 8' for 'del-acl('
+ * '- 9' for 'del-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRQ;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+ cur_arg += 1;
+ } else if (strncmp(args[0], "del-map", 7) == 0) {
+ /* http-request del-map(<reference (map name)>) <key pattern> */
+ rule->action = HTTP_REQ_ACT_DEL_MAP;
+ /*
+ * '+ 8' for 'del-map('
+ * '- 9' for 'del-map(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRQ;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+ cur_arg += 1;
+ } else if (strncmp(args[0], "set-map", 7) == 0) {
+ /* http-request set-map(<reference (map name)>) <key pattern> <value pattern> */
+ rule->action = HTTP_REQ_ACT_SET_MAP;
+ /*
+ * '+ 8' for 'set-map('
+ * '- 9' for 'set-map(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] || !*args[cur_arg+1] ||
+ (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ LIST_INIT(&rule->arg.map.value);
+ proxy->conf.args.ctx = ARGC_HRQ;
+
+ /* key pattern */
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+
+ /* value pattern */
+ parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+
+ cur_arg += 2;
+ } else if (((custom = action_http_req_custom(args[0])) != NULL)) {
+ char *errmsg = NULL;
+ cur_arg = 1;
+ /* try in the module list */
+ if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) < 0) {
+ Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
+ file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
+ free(errmsg);
+ goto out_err;
+ }
} else {
- Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
+ Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}
@@ -8679,6 +9094,7 @@
struct http_res_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
{
struct http_res_rule *rule;
+ struct http_res_action_kw *custom = NULL;
int cur_arg;
rule = calloc(1, sizeof(*rule));
@@ -8816,8 +9232,138 @@
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
+ } else if (strncmp(args[0], "add-acl", 7) == 0) {
+ /* http-request add-acl(<reference (acl name)>) <key pattern> */
+ rule->action = HTTP_RES_ACT_ADD_ACL;
+ /*
+ * '+ 8' for 'add-acl('
+ * '- 9' for 'add-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRS;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+
+ cur_arg += 1;
+ } else if (strncmp(args[0], "del-acl", 7) == 0) {
+ /* http-response del-acl(<reference (acl name)>) <key pattern> */
+ rule->action = HTTP_RES_ACT_DEL_ACL;
+ /*
+ * '+ 8' for 'del-acl('
+ * '- 9' for 'del-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRS;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+ cur_arg += 1;
+ } else if (strncmp(args[0], "del-map", 7) == 0) {
+ /* http-response del-map(<reference (map name)>) <key pattern> */
+ rule->action = HTTP_RES_ACT_DEL_MAP;
+ /*
+ * '+ 8' for 'del-map('
+ * '- 9' for 'del-map(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] ||
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ proxy->conf.args.ctx = ARGC_HRS;
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+ cur_arg += 1;
+ } else if (strncmp(args[0], "set-map", 7) == 0) {
+ /* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
+ rule->action = HTTP_RES_ACT_SET_MAP;
+ /*
+ * '+ 8' for 'set-map('
+ * '- 9' for 'set-map(' + trailing ')'
+ */
+ rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
+
+ cur_arg = 1;
+
+ if (!*args[cur_arg] || !*args[cur_arg+1] ||
+ (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ LIST_INIT(&rule->arg.map.key);
+ LIST_INIT(&rule->arg.map.value);
+
+ proxy->conf.args.ctx = ARGC_HRS;
+
+ /* key pattern */
+ parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+
+ /* value pattern */
+ parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+
+ free(proxy->conf.lfs_file);
+ proxy->conf.lfs_file = strdup(proxy->conf.args.file);
+ proxy->conf.lfs_line = proxy->conf.args.line;
+
+ cur_arg += 2;
+ } else if (((custom = action_http_res_custom(args[0])) != NULL)) {
+ char *errmsg = NULL;
+ cur_arg = 1;
+ /* try in the module list */
+ if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) < 0) {
+ Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
+ file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
+ free(errmsg);
+ goto out_err;
+ }
} else {
- Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
+ Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}
@@ -9123,7 +9669,7 @@
* We use the pre-parsed method if it is known, and store its number as an
* integer. If it is unknown, we use the pointer and the length.
*/
-static int pat_parse_meth(const char *text, struct pattern *pattern, char **err)
+static int pat_parse_meth(const char *text, struct pattern *pattern, int mflags, char **err)
{
int len, meth;
struct chunk *trash;
@@ -9203,7 +9749,7 @@
if (pattern->len != smp->data.meth.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.meth.str.str, smp->data.meth.str.len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.meth.str.str, smp->data.meth.str.len) != 0))
return pattern;
@@ -10026,6 +10572,54 @@
return 1;
}
+/* Retrieves the HTTP version from the request (either 1.0 or 1.1) and emits it
+ * as a string (either "HTTP/1.0" or "HTTP/1.1").
+ */
+static int
+smp_fetch_capture_req_ver(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+ const struct arg *args, struct sample *smp, const char *kw)
+{
+ struct http_txn *txn = l7;
+
+ if (txn->req.msg_state < HTTP_MSG_HDR_FIRST)
+ return 0;
+
+ if (txn->req.flags & HTTP_MSGF_VER_11)
+ smp->data.str.str = "HTTP/1.1";
+ else
+ smp->data.str.str = "HTTP/1.0";
+
+ smp->data.str.len = 8;
+ smp->type = SMP_T_STR;
+ smp->flags = SMP_F_CONST;
+ return 1;
+
+}
+
+/* Retrieves the HTTP version from the response (either 1.0 or 1.1) and emits it
+ * as a string (either "HTTP/1.0" or "HTTP/1.1").
+ */
+static int
+smp_fetch_capture_res_ver(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+ const struct arg *args, struct sample *smp, const char *kw)
+{
+ struct http_txn *txn = l7;
+
+ if (txn->rsp.msg_state < HTTP_MSG_HDR_FIRST)
+ return 0;
+
+ if (txn->rsp.flags & HTTP_MSGF_VER_11)
+ smp->data.str.str = "HTTP/1.1";
+ else
+ smp->data.str.str = "HTTP/1.0";
+
+ smp->data.str.len = 8;
+ smp->type = SMP_T_STR;
+ smp->flags = SMP_F_CONST;
+ return 1;
+
+}
+
/* Iterate over all cookies present in a message. The context is stored in
* smp->ctx.a[0] for the in-header position, smp->ctx.a[1] for the
@@ -10034,10 +10628,10 @@
* The cookie name is in args and the name length in args->data.str.len.
* Accepts exactly 1 argument of type string. If the input options indicate
* that no iterating is desired, then only last value is fetched if any.
- * The returned sample is of type CSTR.
+ * The returned sample is of type CSTR. Can be used to parse cookies in other
+ * files.
*/
-static int
-smp_fetch_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+int smp_fetch_cookie(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
struct http_txn *txn = l7;
@@ -10660,6 +11254,44 @@
return smp->data.str.len != 0;
}
+/*
+ * Return the struct http_req_action_kw associated to a keyword.
+ */
+struct http_req_action_kw *action_http_req_custom(const char *kw)
+{
+ if (!LIST_ISEMPTY(&http_req_keywords.list)) {
+ struct http_req_action_kw_list *kw_list;
+ int i;
+
+ list_for_each_entry(kw_list, &http_req_keywords.list, list) {
+ for (i = 0; kw_list->kw[i].kw != NULL; i++) {
+ if (!strcmp(kw, kw_list->kw[i].kw))
+ return &kw_list->kw[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Return the struct http_res_action_kw associated to a keyword.
+ */
+struct http_res_action_kw *action_http_res_custom(const char *kw)
+{
+ if (!LIST_ISEMPTY(&http_res_keywords.list)) {
+ struct http_res_action_kw_list *kw_list;
+ int i;
+
+ list_for_each_entry(kw_list, &http_res_keywords.list, list) {
+ for (i = 0; kw_list->kw[i].kw != NULL; i++) {
+ if (!strcmp(kw, kw_list->kw[i].kw))
+ return &kw_list->kw[i];
+ }
+ }
+ }
+ return NULL;
+}
+
/************************************************************************/
/* All supported ACL keywords must be declared here. */
/************************************************************************/
@@ -10764,12 +11396,16 @@
{ "base32", smp_fetch_base32, 0, NULL, SMP_T_UINT, SMP_USE_HRQHV },
{ "base32+src", smp_fetch_base32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
- { "capture.req.uri", smp_fetch_capture_req_uri, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
- { "capture.req.method", smp_fetch_capture_req_method, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
-
/* capture are allocated and are permanent in the session */
{ "capture.req.hdr", smp_fetch_capture_header_req, ARG1(1, UINT), NULL, SMP_T_STR, SMP_USE_HRQHP },
+
+ /* retrieve these captures from the HTTP logs */
+ { "capture.req.method", smp_fetch_capture_req_method, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
+ { "capture.req.uri", smp_fetch_capture_req_uri, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
+ { "capture.req.ver", smp_fetch_capture_req_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
+
{ "capture.res.hdr", smp_fetch_capture_header_res, ARG1(1, UINT), NULL, SMP_T_STR, SMP_USE_HRSHP },
+ { "capture.res.ver", smp_fetch_capture_res_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
/* cookie is valid in both directions (eg: for "stick ...") but cook*
* are only here to match the ACL's name, are request-only and are used
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/proto_tcp.c
^
|
@@ -201,14 +201,14 @@
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (foreign_ok) {
- if (is_addr(&bind_addr)) {
+ if (is_inet_addr(&bind_addr)) {
ret = bind(fd, (struct sockaddr *)&bind_addr, get_addr_len(&bind_addr));
if (ret < 0)
return 2;
}
}
else {
- if (is_addr(local)) {
+ if (is_inet_addr(local)) {
ret = bind(fd, (struct sockaddr *)local, get_addr_len(local));
if (ret < 0)
return 1;
@@ -367,7 +367,7 @@
if (src) {
int ret, flags = 0;
- if (is_addr(&conn->addr.from)) {
+ if (is_inet_addr(&conn->addr.from)) {
switch (src->opts & CO_SRC_TPROXY_MASK) {
case CO_SRC_TPROXY_ADDR:
case CO_SRC_TPROXY_CLI:
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/proto_uxst.c
^
|
@@ -37,6 +37,7 @@
#include <types/global.h>
+#include <proto/connection.h>
#include <proto/fd.h>
#include <proto/listener.h>
#include <proto/log.h>
@@ -47,6 +48,7 @@
static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen);
static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen);
static int uxst_unbind_listeners(struct protocol *proto);
+static int uxst_connect_server(struct connection *conn, int data, int delack);
/* Note: must not be declared <const> as its list will be overwritten */
static struct protocol proto_unix = {
@@ -58,6 +60,7 @@
.sock_addrlen = sizeof(struct sockaddr_un),
.l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path),/* path len */
.accept = &listener_accept,
+ .connect = &uxst_connect_server,
.bind = uxst_bind_listener,
.bind_all = uxst_bind_listeners,
.unbind_all = uxst_unbind_listeners,
@@ -185,45 +188,46 @@
if (ext)
goto fd_ready;
- /* 1. create socket names */
- if (!path[0]) {
- msg = "Invalid empty name for a UNIX socket";
- goto err_return;
- }
-
- ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid);
- if (ret < 0 || ret >= MAXPATHLEN) {
- msg = "name too long for UNIX socket";
- goto err_return;
- }
-
- ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid);
- if (ret < 0 || ret >= MAXPATHLEN) {
- msg = "name too long for UNIX socket";
- goto err_return;
- }
-
- /* 2. clean existing orphaned entries */
- if (unlink(tempname) < 0 && errno != ENOENT) {
- msg = "error when trying to unlink previous UNIX socket";
- goto err_return;
- }
-
- if (unlink(backname) < 0 && errno != ENOENT) {
- msg = "error when trying to unlink previous UNIX socket";
- goto err_return;
- }
-
- /* 3. backup existing socket */
- if (link(path, backname) < 0 && errno != ENOENT) {
- msg = "error when trying to preserve previous UNIX socket";
- goto err_return;
+ if (path[0]) {
+ ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ msg = "name too long for UNIX socket";
+ goto err_return;
+ }
+
+ ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ msg = "name too long for UNIX socket";
+ goto err_return;
+ }
+
+ /* 2. clean existing orphaned entries */
+ if (unlink(tempname) < 0 && errno != ENOENT) {
+ msg = "error when trying to unlink previous UNIX socket";
+ goto err_return;
+ }
+
+ if (unlink(backname) < 0 && errno != ENOENT) {
+ msg = "error when trying to unlink previous UNIX socket";
+ goto err_return;
+ }
+
+ /* 3. backup existing socket */
+ if (link(path, backname) < 0 && errno != ENOENT) {
+ msg = "error when trying to preserve previous UNIX socket";
+ goto err_return;
+ }
+
+ strncpy(addr.sun_path, tempname, sizeof(addr.sun_path));
+ addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
+ }
+ else {
+ /* first char is zero, it's an abstract socket whose address
+ * is defined by all the bytes past this zero.
+ */
+ memcpy(addr.sun_path, path, sizeof(addr.sun_path));
}
-
- /* 4. prepare new socket */
addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, tempname, sizeof(addr.sun_path));
- addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
@@ -251,9 +255,9 @@
/* <uid> and <gid> different of -1 will be used to change the socket owner.
* If <mode> is not 0, it will be used to restrict access to the socket.
* While it is known not to be portable on every OS, it's still useful
- * where it works.
+ * where it works. We also don't change permissions on abstract sockets.
*/
- if (!ext &&
+ if (!ext && path[0] &&
(((listener->bind_conf->ux.uid != -1 || listener->bind_conf->ux.gid != -1) &&
(chown(tempname, listener->bind_conf->ux.uid, listener->bind_conf->ux.gid) == -1)) ||
(listener->bind_conf->ux.mode != 0 && chmod(tempname, listener->bind_conf->ux.mode) == -1))) {
@@ -272,21 +276,21 @@
goto err_unlink_temp;
}
- /* 5. install.
- * Point of no return: we are ready, we'll switch the sockets. We don't
+ /* Point of no return: we are ready, we'll switch the sockets. We don't
* fear loosing the socket <path> because we have a copy of it in
- * backname.
+ * backname. Abstract sockets are not renamed.
*/
- if (!ext && rename(tempname, path) < 0) {
+ if (!ext && path[0] && rename(tempname, path) < 0) {
msg = "cannot switch final and temporary UNIX sockets";
goto err_rename;
}
- /* 6. cleanup. If we're bound to an fd inherited from the parent, we
+ /* Cleanup: If we're bound to an fd inherited from the parent, we
* want to ensure that destroy_uxst_socket() will never remove the
- * path, and for this we simply clear the path to the socket.
+ * path, and for this we simply clear the path to the socket, which
+ * under Linux corresponds to an abstract socket.
*/
- if (!ext)
+ if (!ext && path[0])
unlink(backname);
else
((struct sockaddr_un *)&listener->addr)->sun_path[0] = 0;
@@ -347,6 +351,184 @@
proto_unix.nb_listeners++;
}
+
+/*
+ * This function initiates a UNIX connection establishment to the target assigned
+ * to connection <conn> using (si->{target,addr.to}). The source address is ignored
+ * and will be selected by the system. conn->target may point either to a valid
+ * server or to a backend, depending on conn->target. Only OBJ_TYPE_PROXY and
+ * OBJ_TYPE_SERVER are supported. The <data> parameter is a boolean indicating
+ * whether there are data waiting for being sent or not, in order to adjust data
+ * write polling and on some platforms. The <delack> argument is ignored.
+ *
+ * Note that a pending send_proxy message accounts for data.
+ *
+ * It can return one of :
+ * - SN_ERR_NONE if everything's OK
+ * - SN_ERR_SRVTO if there are no more servers
+ * - SN_ERR_SRVCL if the connection was refused by the server
+ * - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
+ * - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
+ * - SN_ERR_INTERNAL for any other purely internal errors
+ * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
+ *
+ * The connection's fd is inserted only when SN_ERR_NONE is returned, otherwise
+ * it's invalid and the caller has nothing to do.
+ */
+int uxst_connect_server(struct connection *conn, int data, int delack)
+{
+ int fd;
+ struct server *srv;
+ struct proxy *be;
+
+ conn->flags = 0;
+
+ switch (obj_type(conn->target)) {
+ case OBJ_TYPE_PROXY:
+ be = objt_proxy(conn->target);
+ srv = NULL;
+ break;
+ case OBJ_TYPE_SERVER:
+ srv = objt_server(conn->target);
+ be = srv->proxy;
+ break;
+ default:
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_INTERNAL;
+ }
+
+ if ((fd = conn->t.sock.fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+ qfprintf(stderr, "Cannot get a server socket.\n");
+
+ if (errno == ENFILE) {
+ conn->err_code = CO_ER_SYS_FDLIM;
+ send_log(be, LOG_EMERG,
+ "Proxy %s reached system FD limit at %d. Please check system tunables.\n",
+ be->id, maxfd);
+ }
+ else if (errno == EMFILE) {
+ conn->err_code = CO_ER_PROC_FDLIM;
+ send_log(be, LOG_EMERG,
+ "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
+ be->id, maxfd);
+ }
+ else if (errno == ENOBUFS || errno == ENOMEM) {
+ conn->err_code = CO_ER_SYS_MEMLIM;
+ send_log(be, LOG_EMERG,
+ "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
+ be->id, maxfd);
+ }
+ else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
+ conn->err_code = CO_ER_NOPROTO;
+ }
+ else
+ conn->err_code = CO_ER_SOCK_ERR;
+
+ /* this is a resource error */
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_RESOURCE;
+ }
+
+ if (fd >= global.maxsock) {
+ /* do not log anything there, it's a normal condition when this option
+ * is used to serialize connections to a server !
+ */
+ Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
+ close(fd);
+ conn->err_code = CO_ER_CONF_FDLIM;
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_PRXCOND; /* it is a configuration limit */
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
+ close(fd);
+ conn->err_code = CO_ER_SOCK_ERR;
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_INTERNAL;
+ }
+
+ /* if a send_proxy is there, there are data */
+ data |= conn->send_proxy_ofs;
+
+ if (global.tune.server_sndbuf)
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
+
+ if (global.tune.server_rcvbuf)
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
+
+ if (connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) == -1) {
+ if (errno == EALREADY || errno == EISCONN) {
+ conn->flags &= ~CO_FL_WAIT_L4_CONN;
+ }
+ else if (errno == EINPROGRESS) {
+ conn->flags |= CO_FL_WAIT_L4_CONN;
+ }
+ else if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
+ char *msg;
+ if (errno == EAGAIN || errno == EADDRNOTAVAIL) {
+ msg = "no free ports";
+ conn->err_code = CO_ER_FREE_PORTS;
+ }
+ else {
+ msg = "local address already in use";
+ conn->err_code = CO_ER_ADDR_INUSE;
+ }
+
+ qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg);
+ close(fd);
+ send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg);
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_RESOURCE;
+ }
+ else if (errno == ETIMEDOUT) {
+ close(fd);
+ conn->err_code = CO_ER_SOCK_ERR;
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_SRVTO;
+ }
+ else { // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
+ close(fd);
+ conn->err_code = CO_ER_SOCK_ERR;
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_SRVCL;
+ }
+ }
+ else {
+ /* connect() already succeeded, which is quite usual for unix
+ * sockets. Let's avoid a second connect() probe to complete it,
+ * but we need to ensure we'll wake up if there's no more handshake
+ * pending (eg: for health checks).
+ */
+ conn->flags &= ~CO_FL_WAIT_L4_CONN;
+ if (!(conn->flags & CO_FL_HANDSHAKE))
+ data = 1;
+ }
+
+ conn->flags |= CO_FL_ADDR_TO_SET;
+
+ /* Prepare to send a few handshakes related to the on-wire protocol. */
+ if (conn->send_proxy_ofs)
+ conn->flags |= CO_FL_SEND_PROXY;
+
+ conn_ctrl_init(conn); /* registers the FD */
+ fdtab[fd].linger_risk = 0; /* no need to disable lingering */
+ if (conn->flags & CO_FL_HANDSHAKE)
+ conn_sock_want_send(conn); /* for connect status or proxy protocol */
+
+ if (conn_xprt_init(conn) < 0) {
+ conn_force_close(conn);
+ conn->flags |= CO_FL_ERROR;
+ return SN_ERR_RESOURCE;
+ }
+
+ if (data)
+ conn_data_want_send(conn); /* prepare to send data if any */
+
+ return SN_ERR_NONE; /* connection is OK */
+}
+
+
/********************************
* 3) protocol-oriented functions
********************************/
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/proxy.c
^
|
@@ -139,6 +139,7 @@
const char *res, *name;
int *tv = NULL;
int *td = NULL;
+ int warn = 0;
retval = 0;
@@ -147,7 +148,7 @@
args++;
name = args[0];
- if (!strcmp(args[0], "client") || !strcmp(args[0], "clitimeout")) {
+ if (!strcmp(args[0], "client") || (!strcmp(args[0], "clitimeout") && (warn = WARN_CLITO_DEPRECATED))) {
name = "client";
tv = &proxy->timeout.client;
td = &defpx->timeout.client;
@@ -164,12 +165,12 @@
tv = &proxy->timeout.httpreq;
td = &defpx->timeout.httpreq;
cap = PR_CAP_FE | PR_CAP_BE;
- } else if (!strcmp(args[0], "server") || !strcmp(args[0], "srvtimeout")) {
+ } else if (!strcmp(args[0], "server") || (!strcmp(args[0], "srvtimeout") && (warn = WARN_SRVTO_DEPRECATED))) {
name = "server";
tv = &proxy->timeout.server;
td = &defpx->timeout.server;
cap = PR_CAP_BE;
- } else if (!strcmp(args[0], "connect") || !strcmp(args[0], "contimeout")) {
+ } else if (!strcmp(args[0], "connect") || (!strcmp(args[0], "contimeout") && (warn = WARN_CONTO_DEPRECATED))) {
name = "connect";
tv = &proxy->timeout.connect;
td = &defpx->timeout.connect;
@@ -186,10 +187,19 @@
tv = &proxy->timeout.tunnel;
td = &defpx->timeout.tunnel;
cap = PR_CAP_BE;
+ } else if (!strcmp(args[0], "client-fin")) {
+ tv = &proxy->timeout.clientfin;
+ td = &defpx->timeout.clientfin;
+ cap = PR_CAP_FE;
+ } else if (!strcmp(args[0], "server-fin")) {
+ tv = &proxy->timeout.serverfin;
+ td = &defpx->timeout.serverfin;
+ cap = PR_CAP_BE;
} else {
memprintf(err,
"'timeout' supports 'client', 'server', 'connect', 'check', "
- "'queue', 'http-keep-alive', 'http-request', 'tunnel' or 'tarpit', (got '%s')",
+ "'queue', 'http-keep-alive', 'http-request', 'tunnel', 'tarpit', "
+ "'client-fin' and 'server-fin' (got '%s')",
args[0]);
return -1;
}
@@ -215,6 +225,13 @@
memprintf(err, "overwriting 'timeout %s' which was already specified", name);
retval = 1;
}
+ else if (warn) {
+ if (!already_warned(warn)) {
+ memprintf(err, "the '%s' directive is now deprecated in favor of 'timeout %s', and will not be supported in future versions.",
+ args[0], name);
+ retval = 1;
+ }
+ }
*tv = MS_TO_TICKS(timeout);
return retval;
@@ -275,6 +292,45 @@
return retval;
}
+/* This function parses a "max-keep-alive-queue" statement in a proxy section.
+ * It returns -1 if there is any error, 1 for a warning, otherwise zero. If it
+ * does not return zero, it will write an error or warning message into a
+ * preallocated buffer returned at <err>. The function must be called with
+ * <args> pointing to the first command line word, with <proxy> pointing to
+ * the proxy being parsed, and <defpx> to the default proxy or NULL.
+ */
+static int proxy_parse_max_ka_queue(char **args, int section, struct proxy *proxy,
+ struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ int retval;
+ char *res;
+ unsigned int val;
+
+ retval = 0;
+
+ if (*args[1] == 0) {
+ memprintf(err, "'%s' expects expects an integer value (or -1 to disable)", args[0]);
+ return -1;
+ }
+
+ val = strtol(args[1], &res, 0);
+ if (*res) {
+ memprintf(err, "'%s' : unexpected character '%c' in integer value '%s'", args[0], *res, args[1]);
+ return -1;
+ }
+
+ if (!(proxy->cap & PR_CAP_BE)) {
+ memprintf(err, "%s will be ignored because %s '%s' has no backend capability",
+ args[0], proxy_type_str(proxy), proxy->id);
+ retval = 1;
+ }
+
+ /* we store <val+1> so that a user-facing value of -1 is stored as zero (default) */
+ proxy->max_ka_queue = val + 1;
+ return retval;
+}
+
/* This function inserts proxy <px> into the tree of known proxies. The proxy's
* name is used as the storing key so it must already have been initialized.
*/
@@ -467,7 +523,7 @@
LIST_INIT(&p->acl);
LIST_INIT(&p->http_req_rules);
LIST_INIT(&p->http_res_rules);
- LIST_INIT(&p->block_cond);
+ LIST_INIT(&p->block_rules);
LIST_INIT(&p->redirect_rules);
LIST_INIT(&p->mon_fail_cond);
LIST_INIT(&p->switching_rules);
@@ -886,14 +942,12 @@
* a struct hdr_idx for it if we did not have one.
*/
if (unlikely(!s->txn.hdr_idx.v && be->http_needed)) {
+ s->txn.hdr_idx.size = global.tune.max_http_hdr;
if ((s->txn.hdr_idx.v = pool_alloc2(pool2_hdr_idx)) == NULL)
return 0; /* not enough memory */
/* and now initialize the HTTP transaction state */
http_init_txn(s);
-
- s->txn.hdr_idx.size = global.tune.max_http_hdr;
- hdr_idx_init(&s->txn.hdr_idx);
}
/* If an LB algorithm needs to access some pre-parsed body contents,
@@ -926,6 +980,7 @@
{ CFG_LISTEN, "contimeout", proxy_parse_timeout },
{ CFG_LISTEN, "srvtimeout", proxy_parse_timeout },
{ CFG_LISTEN, "rate-limit", proxy_parse_rate_limit },
+ { CFG_LISTEN, "max-keep-alive-queue", proxy_parse_max_ka_queue },
{ 0, NULL, NULL },
}};
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/sample.c
^
|
@@ -20,6 +20,7 @@
#include <common/chunk.h>
#include <common/standard.h>
#include <common/uri_auth.h>
+#include <common/base64.h>
#include <proto/arg.h>
#include <proto/auth.h>
@@ -1172,6 +1173,23 @@
/* These functions set the data type on return. */
/*****************************************************************/
+static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp)
+{
+ struct chunk *trash = get_trash_chunk();
+ int b64_len;
+
+ trash->len = 0;
+ b64_len = a2base64(smp->data.str.str, smp->data.str.len, trash->str, trash->size);
+ if (b64_len < 0)
+ return 0;
+
+ trash->len = b64_len;
+ smp->data.str = *trash;
+ smp->type = SMP_T_STR;
+ smp->flags &= ~SMP_F_CONST;
+ return 1;
+}
+
static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp)
{
struct chunk *trash = get_trash_chunk();
@@ -1329,6 +1347,7 @@
/* Note: must not be declared <const> as its list will be overwritten */
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+ { "base64", sample_conv_bin2base64,0, NULL, SMP_T_BIN, SMP_T_STR },
{ "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "hex", sample_conv_bin2hex, 0, NULL, SMP_T_BIN, SMP_T_STR },
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/server.c
^
|
@@ -597,6 +597,7 @@
}
newsrv->check_common.addr = *sk;
+ newsrv->check_common.proto = protocol_by_family(sk->ss_family);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "port")) {
@@ -612,7 +613,11 @@
cur_arg ++;
}
else if (!defsrv && !strcmp(args[cur_arg], "send-proxy")) {
- newsrv->state |= SRV_SEND_PROXY;
+ newsrv->pp_opts |= SRV_PP_V1;
+ cur_arg ++;
+ }
+ else if (!defsrv && !strcmp(args[cur_arg], "send-proxy-v2")) {
+ newsrv->pp_opts |= SRV_PP_V2;
cur_arg ++;
}
else if (!defsrv && !strcmp(args[cur_arg], "check-send-proxy")) {
@@ -824,7 +829,7 @@
if (!strcmp(args[cur_arg], "usesrc")) { /* address to use outside */
#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
#if !defined(CONFIG_HAP_TRANSPARENT)
- if (!is_addr(&newsrv->conn_src.source_addr)) {
+ if (!is_inet_addr(&newsrv->conn_src.source_addr)) {
Alert("parsing [%s:%d] : '%s' requires an explicit '%s' address.\n",
file, linenum, "usesrc", "source");
err_code |= ERR_ALERT | ERR_FATAL;
@@ -1043,7 +1048,7 @@
#ifdef USE_OPENSSL
newsrv->check.use_ssl |= (newsrv->use_ssl || (newsrv->proxy->options & PR_O_TCPCHK_SSL));
#endif
- newsrv->check.send_proxy |= (newsrv->state & SRV_SEND_PROXY);
+ newsrv->check.send_proxy |= (newsrv->pp_opts);
}
/* try to get the port from check_core.addr if check.port not set */
if (!newsrv->check.port)
@@ -1067,9 +1072,11 @@
}
/*
* We need at least a service port, a check port or the first tcp-check rule must
- * be a 'connect' one
+ * be a 'connect' one when checking an IPv4/IPv6 server.
*/
- if (!newsrv->check.port) {
+ if (!newsrv->check.port &&
+ (is_inet_addr(&newsrv->check_common.addr) ||
+ (!is_addr(&newsrv->check_common.addr) && is_inet_addr(&newsrv->addr)))) {
struct tcpcheck_rule *n = NULL, *r = NULL;
struct list *l;
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/session.c
^
|
@@ -246,7 +246,8 @@
/* prepare the trash with a log prefix for session <s>. It only works with
- * embryonic sessions based on a real connection.
+ * embryonic sessions based on a real connection. This function requires that
+ * at s->target still points to the incoming connection.
*/
static void prepare_mini_sess_log_prefix(struct session *s)
{
@@ -275,7 +276,8 @@
/* This function kills an existing embryonic session. It stops the connection's
* transport layer, releases assigned resources, resumes the listener if it was
- * disabled and finally kills the file descriptor.
+ * disabled and finally kills the file descriptor. This function requires that
+ * at s->target still points to the incoming connection.
*/
static void kill_mini_session(struct session *s)
{
@@ -387,7 +389,9 @@
* be called with an embryonic session. It returns a positive value upon
* success, 0 if the connection can be ignored, or a negative value upon
* critical failure. The accepted file descriptor is closed if we return <= 0.
- * The client-side end point is assumed to be a connection.
+ * The client-side end point is assumed to be a connection, whose pointer is
+ * taken from s->target which is assumed to be valid. If the function fails,
+ * it restores s->target.
*/
int session_complete(struct session *s)
{
@@ -443,7 +447,11 @@
si_reset(&s->si[0], t);
si_set_state(&s->si[0], SI_ST_EST);
- /* attach the incoming connection to the stream interface now */
+ /* attach the incoming connection to the stream interface now.
+ * We must do that *before* clearing ->target because we need
+ * to keep a pointer to the connection in case we have to call
+ * kill_mini_session().
+ */
si_attach_conn(&s->si[0], conn);
if (likely(s->fe->options2 & PR_O2_INDEPSTR))
@@ -568,6 +576,10 @@
out_free_req:
pool_free2(pool2_channel, s->req);
out_free_task:
+ /* and restore the connection pointer in case we destroyed it,
+ * because kill_mini_session() will need it.
+ */
+ s->target = &conn->obj_type;
return ret;
}
@@ -932,6 +944,10 @@
rep->analysers |= s->fe->fe_rsp_ana | s->be->be_rsp_ana;
rep->flags |= CF_READ_ATTACHED; /* producer is now attached */
+ if (req->flags & CF_WAKE_CONNECT) {
+ req->flags |= CF_WAKE_ONCE;
+ req->flags &= ~CF_WAKE_CONNECT;
+ }
if (objt_conn(si->end)) {
/* real connections have timeouts */
req->wto = s->be->timeout.server;
@@ -2173,11 +2189,17 @@
/* first, let's check if the request buffer needs to shutdown(write), which may
* happen either because the input is closed or because we want to force a close
- * once the server has begun to respond.
+ * once the server has begun to respond. If a half-closed timeout is set, we adjust
+ * the other side's timeout as well.
*/
if (unlikely((s->req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
- (CF_AUTO_CLOSE|CF_SHUTR)))
- channel_shutw_now(s->req);
+ (CF_AUTO_CLOSE|CF_SHUTR))) {
+ channel_shutw_now(s->req);
+ if (tick_isset(s->fe->timeout.clientfin)) {
+ s->rep->wto = s->fe->timeout.clientfin;
+ s->rep->wex = tick_add(now_ms, s->rep->wto);
+ }
+ }
/* shutdown(write) pending */
if (unlikely((s->req->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
@@ -2185,6 +2207,10 @@
if (s->req->flags & CF_READ_ERROR)
s->req->cons->flags |= SI_FL_NOLINGER;
si_shutw(s->req->cons);
+ if (tick_isset(s->be->timeout.serverfin)) {
+ s->rep->rto = s->be->timeout.serverfin;
+ s->rep->rex = tick_add(now_ms, s->rep->rto);
+ }
}
/* shutdown(write) done on server side, we must stop the client too */
@@ -2197,6 +2223,10 @@
if (s->req->prod->flags & SI_FL_NOHALF)
s->req->prod->flags |= SI_FL_NOLINGER;
si_shutr(s->req->prod);
+ if (tick_isset(s->fe->timeout.clientfin)) {
+ s->rep->wto = s->fe->timeout.clientfin;
+ s->rep->wex = tick_add(now_ms, s->rep->wto);
+ }
}
/* it's possible that an upper layer has requested a connection setup or abort.
@@ -2292,13 +2322,26 @@
channel_forward(s->rep, CHN_INFINITE_FORWARD);
/* if we have no analyser anymore in any direction and have a
- * tunnel timeout set, use it now.
+ * tunnel timeout set, use it now. Note that we must respect
+ * the half-closed timeouts as well.
*/
if (!s->req->analysers && s->be->timeout.tunnel) {
s->req->rto = s->req->wto = s->rep->rto = s->rep->wto =
s->be->timeout.tunnel;
- s->req->rex = s->req->wex = s->rep->rex = s->rep->wex =
- tick_add(now_ms, s->be->timeout.tunnel);
+
+ if ((s->req->flags & CF_SHUTR) && tick_isset(s->fe->timeout.clientfin))
+ s->rep->wto = s->fe->timeout.clientfin;
+ if ((s->req->flags & CF_SHUTW) && tick_isset(s->be->timeout.serverfin))
+ s->rep->rto = s->be->timeout.serverfin;
+ if ((s->rep->flags & CF_SHUTR) && tick_isset(s->be->timeout.serverfin))
+ s->req->wto = s->be->timeout.serverfin;
+ if ((s->rep->flags & CF_SHUTW) && tick_isset(s->fe->timeout.clientfin))
+ s->req->rto = s->fe->timeout.clientfin;
+
+ s->req->rex = tick_add(now_ms, s->req->rto);
+ s->req->wex = tick_add(now_ms, s->req->wto);
+ s->rep->rex = tick_add(now_ms, s->rep->rto);
+ s->rep->wex = tick_add(now_ms, s->rep->wto);
}
}
@@ -2328,13 +2371,23 @@
/* first, let's check if the response buffer needs to shutdown(write) */
if (unlikely((s->rep->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
- (CF_AUTO_CLOSE|CF_SHUTR)))
+ (CF_AUTO_CLOSE|CF_SHUTR))) {
channel_shutw_now(s->rep);
+ if (tick_isset(s->be->timeout.serverfin)) {
+ s->req->wto = s->be->timeout.serverfin;
+ s->req->wex = tick_add(now_ms, s->req->wto);
+ }
+ }
/* shutdown(write) pending */
if (unlikely((s->rep->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
- channel_is_empty(s->rep)))
+ channel_is_empty(s->rep))) {
si_shutw(s->rep->cons);
+ if (tick_isset(s->fe->timeout.clientfin)) {
+ s->req->rto = s->fe->timeout.clientfin;
+ s->req->rex = tick_add(now_ms, s->req->rto);
+ }
+ }
/* shutdown(write) done on the client side, we must stop the server too */
if (unlikely((s->rep->flags & (CF_SHUTW|CF_SHUTR|CF_SHUTR_NOW)) == CF_SHUTW) &&
@@ -2346,6 +2399,10 @@
if (s->rep->prod->flags & SI_FL_NOHALF)
s->rep->prod->flags |= SI_FL_NOLINGER;
si_shutr(s->rep->prod);
+ if (tick_isset(s->be->timeout.serverfin)) {
+ s->req->wto = s->be->timeout.serverfin;
+ s->req->wex = tick_add(now_ms, s->req->wto);
+ }
}
if (s->req->prod->state == SI_ST_DIS || s->req->cons->state == SI_ST_DIS)
@@ -2407,20 +2464,6 @@
s->si[0].flags &= ~(SI_FL_ERR|SI_FL_EXP);
s->si[1].flags &= ~(SI_FL_ERR|SI_FL_EXP);
- /* Trick: if a request is being waiting for the server to respond,
- * and if we know the server can timeout, we don't want the timeout
- * to expire on the client side first, but we're still interested
- * in passing data from the client to the server (eg: POST). Thus,
- * we can cancel the client's request timeout if the server's
- * request timeout is set and the server has not yet sent a response.
- */
-
- if ((s->rep->flags & (CF_AUTO_CLOSE|CF_SHUTR)) == 0 &&
- (tick_isset(s->req->wex) || tick_isset(s->rep->rex))) {
- s->req->flags |= CF_READ_NOEXP;
- s->req->rex = TICK_ETERNITY;
- }
-
/* When any of the stream interfaces is attached to an applet,
* we have to call it here. Note that this one may wake the
* task up again. If at least one applet was called, the current
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/shctx.c
^
|
@@ -13,6 +13,9 @@
#include <sys/mman.h>
#ifndef USE_PRIVATE_CACHE
+#ifdef USE_PTHREAD_PSHARED
+#include <pthread.h>
+#else
#ifdef USE_SYSCALL_FUTEX
#include <unistd.h>
#ifndef u32
@@ -20,9 +23,8 @@
#endif
#include <linux/futex.h>
#include <sys/syscall.h>
-#else /* USE_SYSCALL_FUTEX */
-#include <pthread.h>
-#endif /* USE_SYSCALL_FUTEX */
+#endif
+#endif
#endif
#include <arpa/inet.h>
#include "ebmbtree.h"
@@ -60,10 +62,10 @@
struct shared_context {
#ifndef USE_PRIVATE_CACHE
-#ifdef USE_SYSCALL_FUTEX
- unsigned int waiters;
-#else /* USE_SYSCALL_FUTEX */
+#ifdef USE_PTHREAD_PSHARED
pthread_mutex_t mutex;
+#else
+ unsigned int waiters;
#endif
#endif
struct shsess_packet_hdr upd;
@@ -75,17 +77,63 @@
/* Static shared context */
static struct shared_context *shctx = NULL;
-#ifndef USE_PRIVATE_CACHE
-static int use_shared_mem = 0;
-#endif
/* Lock functions */
-#ifdef USE_PRIVATE_CACHE
+
+#if defined (USE_PRIVATE_CACHE)
+
#define shared_context_lock()
#define shared_context_unlock()
+#elif defined (USE_PTHREAD_PSHARED)
+static int use_shared_mem = 0;
+
+#define shared_context_lock() if (use_shared_mem) pthread_mutex_lock(&shctx->mutex)
+#define shared_context_unlock() if (use_shared_mem) pthread_mutex_unlock(&shctx->mutex)
+
#else
+static int use_shared_mem = 0;
+
#ifdef USE_SYSCALL_FUTEX
+static inline void _shared_context_wait4lock(unsigned int *count, unsigned int *uaddr, int value)
+{
+ syscall(SYS_futex, uaddr, FUTEX_WAIT, value, NULL, 0, 0);
+}
+
+static inline void _shared_context_awakelocker(unsigned int *uaddr)
+{
+ syscall(SYS_futex, uaddr, FUTEX_WAKE, 1, NULL, 0, 0);
+}
+
+#else /* internal spin lock */
+
+#if defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (__x86_64__)
+static inline void relax()
+{
+ __asm volatile("rep;nop\n" ::: "memory");
+}
+#else /* if no x86_64 or i586 arch: use less optimized but generic asm */
+static inline void relax()
+{
+ __asm volatile("" ::: "memory");
+}
+#endif
+
+static inline void _shared_context_wait4lock(unsigned int *count, unsigned int *uaddr, int value)
+{
+ int i;
+
+ for (i = 0; i < *count; i++) {
+ relax();
+ relax();
+ }
+ *count = *count << 1;
+}
+
+#define _shared_context_awakelocker(a)
+
+#endif
+
#if defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (__x86_64__)
static inline unsigned int xchg(unsigned int *ptr, unsigned int x)
{
@@ -139,6 +187,7 @@
static inline void _shared_context_lock(void)
{
unsigned int x;
+ unsigned int count = 4;
x = cmpxchg(&shctx->waiters, 0, 1);
if (x) {
@@ -146,7 +195,7 @@
x = xchg(&shctx->waiters, 2);
while (x) {
- syscall(SYS_futex, &shctx->waiters, FUTEX_WAIT, 2, NULL, 0, 0);
+ _shared_context_wait4lock(&count, &shctx->waiters, 2);
x = xchg(&shctx->waiters, 2);
}
}
@@ -156,7 +205,7 @@
{
if (atomic_dec(&shctx->waiters)) {
shctx->waiters = 0;
- syscall(SYS_futex, &shctx->waiters, FUTEX_WAKE, 1, NULL, 0, 0);
+ _shared_context_awakelocker(&shctx->waiters);
}
}
@@ -164,13 +213,6 @@
#define shared_context_unlock() if (use_shared_mem) _shared_context_unlock()
-#else /* USE_SYSCALL_FUTEX */
-
-#define shared_context_lock() if (use_shared_mem) pthread_mutex_lock(&shctx->mutex)
-
-#define shared_context_unlock() if (use_shared_mem) pthread_mutex_unlock(&shctx->mutex)
-
-#endif
#endif
/* List Macros */
@@ -508,9 +550,9 @@
{
int i;
#ifndef USE_PRIVATE_CACHE
-#ifndef USE_SYSCALL_FUTEX
+#ifdef USE_PTHREAD_PSHARED
pthread_mutexattr_t attr;
-#endif /* USE_SYSCALL_FUTEX */
+#endif
#endif
struct shared_block *prev,*cur;
int maptype = MAP_PRIVATE;
@@ -532,19 +574,36 @@
PROT_READ | PROT_WRITE, maptype | MAP_ANON, -1, 0);
if (!shctx || shctx == MAP_FAILED) {
shctx = NULL;
- return -1;
+ return SHCTX_E_ALLOC_CACHE;
}
#ifndef USE_PRIVATE_CACHE
-#ifdef USE_SYSCALL_FUTEX
- shctx->waiters = 0;
+ if (maptype == MAP_SHARED) {
+#ifdef USE_PTHREAD_PSHARED
+ if (pthread_mutexattr_init(&attr)) {
+ munmap(shctx, sizeof(struct shared_context)+(size*sizeof(struct shared_block)));
+ shctx = NULL;
+ return SHCTX_E_INIT_LOCK;
+ }
+
+ if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)) {
+ pthread_mutexattr_destroy(&attr);
+ munmap(shctx, sizeof(struct shared_context)+(size*sizeof(struct shared_block)));
+ shctx = NULL;
+ return SHCTX_E_INIT_LOCK;
+ }
+
+ if (pthread_mutex_init(&shctx->mutex, &attr)) {
+ pthread_mutexattr_destroy(&attr);
+ munmap(shctx, sizeof(struct shared_context)+(size*sizeof(struct shared_block)));
+ shctx = NULL;
+ return SHCTX_E_INIT_LOCK;
+ }
#else
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
- pthread_mutex_init(&shctx->mutex, &attr);
+ shctx->waiters = 0;
#endif
- if (maptype == MAP_SHARED)
use_shared_mem = 1;
+ }
#endif
memset(&shctx->active.data.session.key, 0, sizeof(struct ebmb_node));
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/ssl_sock.c
^
|
@@ -45,7 +45,6 @@
#include <openssl/err.h>
#include <openssl/rand.h>
-#include <common/base64.h>
#include <common/buffer.h>
#include <common/compat.h>
#include <common/config.h>
@@ -79,6 +78,8 @@
#define SSL_SOCK_ST_FL_VERIFY_DONE 0x00000001
#define SSL_SOCK_ST_FL_16K_WBFSIZE 0x00000002
#define SSL_SOCK_SEND_UNLIMITED 0x00000004
+#define SSL_SOCK_RECV_HEARTBEAT 0x00000008
+
/* bits 0xFFFF0000 are reserved to store verify errors */
/* Verify errors macros */
@@ -180,6 +181,49 @@
return 0;
}
+/* Callback is called for ssl protocol analyse */
+void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
+{
+#ifdef TLS1_RT_HEARTBEAT
+ /* test heartbeat received (write_p is set to 0
+ for a received record) */
+ if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
+ struct connection *conn = (struct connection *)SSL_get_app_data(ssl);
+ const unsigned char *p = buf;
+ unsigned int payload;
+
+ conn->xprt_st |= SSL_SOCK_RECV_HEARTBEAT;
+
+ /* Check if this is a CVE-2014-0160 exploitation attempt. */
+ if (*p != TLS1_HB_REQUEST)
+ return;
+
+ if (len < 1 + 2 + 16) /* 1 type + 2 size + 0 payload + 16 padding */
+ goto kill_it;
+
+ payload = (p[1] * 256) + p[2];
+ if (3 + payload + 16 <= len)
+ return; /* OK no problem */
+ kill_it:
+ /* We have a clear heartbleed attack (CVE-2014-0160), the
+ * advertised payload is larger than the advertised packet
+ * length, so we have garbage in the buffer between the
+ * payload and the end of the buffer (p+len). We can't know
+ * if the SSL stack is patched, and we don't know if we can
+ * safely wipe out the area between p+3+len and payload.
+ * So instead, we prevent the response from being sent by
+ * setting the max_send_fragment to 0 and we report an SSL
+ * error, which will kill this connection. It will be reported
+ * above as SSL_ERROR_SSL while an other handshake failure with
+ * a heartbeat message will be reported as SSL_ERROR_SYSCALL.
+ */
+ ssl->max_send_fragment = 0;
+ SSLerr(SSL_F_TLS1_HEARTBEAT, SSL_R_SSL_HANDSHAKE_FAILURE);
+ return;
+ }
+#endif
+}
+
#ifdef OPENSSL_NPN_NEGOTIATED
/* This callback is used so that the server advertises the list of
* negociable protocols for NPN.
@@ -270,149 +314,36 @@
#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
#ifndef OPENSSL_NO_DH
-
-static DH *ssl_get_dh_1024(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc2409_prime_1024(NULL);
- /* See RFC 2409, Section 6 "Oakley Groups"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-static DH *ssl_get_dh_2048(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc3526_prime_2048(NULL);
- /* See RFC 3526, Section 3 "2048-bit MODP Group"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-static DH *ssl_get_dh_3072(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc3526_prime_3072(NULL);
- /* See RFC 3526, Section 4 "3072-bit MODP Group"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-static DH *ssl_get_dh_4096(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc3526_prime_4096(NULL);
- /* See RFC 3526, Section 5 "4096-bit MODP Group"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-static DH *ssl_get_dh_6144(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc3526_prime_6144(NULL);
- /* See RFC 3526, Section 6 "6144-bit MODP Group"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-static DH *ssl_get_dh_8192(void)
-{
- DH *dh = DH_new();
- if (dh) {
- dh->p = get_rfc3526_prime_8192(NULL);
- /* See RFC 3526, Section 7 "8192-bit MODP Group"
- for the reason why we use 2 as a generator.
- */
- BN_dec2bn(&dh->g, "2");
- if (!dh->p || !dh->g) {
- DH_free(dh);
- dh = NULL;
- }
- }
- return dh;
-}
-
-/* Returns Diffie-Hellman parameters matching the private key length */
-static DH *ssl_get_tmp_dh(SSL *ssl, int export, int keylen)
-{
- DH *dh = NULL;
- EVP_PKEY *pkey = SSL_get_privatekey(ssl);
- int type = pkey ? EVP_PKEY_type(pkey->type) : EVP_PKEY_NONE;
-
- if (type == EVP_PKEY_RSA || type == EVP_PKEY_DSA) {
- keylen = EVP_PKEY_bits(pkey);
- }
-
- if (keylen >= 8192) {
- dh = ssl_get_dh_8192();
- }
- else if (keylen >= 6144) {
- dh = ssl_get_dh_6144();
- }
- else if (keylen >= 4096) {
- dh = ssl_get_dh_4096();
- }
- else if (keylen >= 3072) {
- dh = ssl_get_dh_3072();
- }
- else if (keylen >= 2048) {
- dh = ssl_get_dh_2048();
- }
- else {
- dh = ssl_get_dh_1024();
- }
-
- return dh;
-}
-
/* Loads Diffie-Hellman parameter from a file. Returns 1 if loaded, else -1
if an error occured, and 0 if parameter not found. */
-static int ssl_sock_load_dh_params(SSL_CTX *ctx, const char *file)
+int ssl_sock_load_dh_params(SSL_CTX *ctx, const char *file)
{
int ret = -1;
BIO *in;
DH *dh = NULL;
+ /* If not present, use parameters generated using 'openssl dhparam 1024 -C':
+ * -----BEGIN DH PARAMETERS-----
+ * MIGHAoGBAJJAJDXDoS5E03MNjnjK36eOL1tRqVa/9NuOVlI+lpXmPjJQbP65EvKn
+ * fSLnG7VMhoCJO4KtG88zf393ltP7loGB2bofcDSr+x+XsxBM8yA/Zj6BmQt+CQ9s
+ * TF7hoOV+wXTT6ErZ5y5qx9pq6hLfKXwTGFT78hrE6HnCO7xgtPdTAgEC
+ * -----END DH PARAMETERS-----
+ */
+ static const unsigned char dh1024_p[] = {
+ 0x92, 0x40, 0x24, 0x35, 0xC3, 0xA1, 0x2E, 0x44, 0xD3, 0x73, 0x0D, 0x8E,
+ 0x78, 0xCA, 0xDF, 0xA7, 0x8E, 0x2F, 0x5B, 0x51, 0xA9, 0x56, 0xBF, 0xF4,
+ 0xDB, 0x8E, 0x56, 0x52, 0x3E, 0x96, 0x95, 0xE6, 0x3E, 0x32, 0x50, 0x6C,
+ 0xFE, 0xB9, 0x12, 0xF2, 0xA7, 0x7D, 0x22, 0xE7, 0x1B, 0xB5, 0x4C, 0x86,
+ 0x80, 0x89, 0x3B, 0x82, 0xAD, 0x1B, 0xCF, 0x33, 0x7F, 0x7F, 0x77, 0x96,
+ 0xD3, 0xFB, 0x96, 0x81, 0x81, 0xD9, 0xBA, 0x1F, 0x70, 0x34, 0xAB, 0xFB,
+ 0x1F, 0x97, 0xB3, 0x10, 0x4C, 0xF3, 0x20, 0x3F, 0x66, 0x3E, 0x81, 0x99,
+ 0x0B, 0x7E, 0x09, 0x0F, 0x6C, 0x4C, 0x5E, 0xE1, 0xA0, 0xE5, 0x7E, 0xC1,
+ 0x74, 0xD3, 0xE8, 0x4A, 0xD9, 0xE7, 0x2E, 0x6A, 0xC7, 0xDA, 0x6A, 0xEA,
+ 0x12, 0xDF, 0x29, 0x7C, 0x13, 0x18, 0x54, 0xFB, 0xF2, 0x1A, 0xC4, 0xE8,
+ 0x79, 0xC2, 0x3B, 0xBC, 0x60, 0xB4, 0xF7, 0x53,
+ };
+ static const unsigned char dh1024_g[] = {
+ 0x02,
+ };
in = BIO_new(BIO_s_file());
if (in == NULL)
@@ -422,17 +353,28 @@
goto end;
dh = PEM_read_bio_DHparams(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata);
- if (dh) {
- ret = 1;
- SSL_CTX_set_tmp_dh(ctx, dh);
- }
- else {
+ if (!dh) {
/* Clear openssl global errors stack */
ERR_clear_error();
- SSL_CTX_set_tmp_dh_callback(ctx, ssl_get_tmp_dh);
+ dh = DH_new();
+ if (dh == NULL)
+ goto end;
+
+ dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+ if (dh->p == NULL)
+ goto end;
+
+ dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
+ if (dh->g == NULL)
+ goto end;
+
ret = 0; /* DH params not found */
}
+ else
+ ret = 1;
+
+ SSL_CTX_set_tmp_dh(ctx, dh);
end:
if (dh)
@@ -887,6 +829,10 @@
}
SSL_CTX_set_info_callback(ctx, ssl_sock_infocbk);
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ SSL_CTX_set_msg_callback(ctx, ssl_sock_msgcbk);
+#endif
+
#ifdef OPENSSL_NPN_NEGOTIATED
if (bind_conf->npn_str)
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, bind_conf);
@@ -1394,13 +1340,26 @@
if (!errno && conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
if (!conn->err_code) {
- if (!((SSL *)conn->xprt_ctx)->packet_length)
- if (!errno)
- conn->err_code = CO_ER_SSL_EMPTY;
+ if (!((SSL *)conn->xprt_ctx)->packet_length) {
+ if (!errno) {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
+ else
+ conn->err_code = CO_ER_SSL_EMPTY;
+ }
+ else {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
+ else
+ conn->err_code = CO_ER_SSL_ABORT;
+ }
+ }
+ else {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
else
- conn->err_code = CO_ER_SSL_ABORT;
- else
- conn->err_code = CO_ER_SSL_HANDSHAKE;
+ conn->err_code = CO_ER_SSL_HANDSHAKE;
+ }
}
goto out_error;
}
@@ -1413,7 +1372,8 @@
*/
conn_drain(conn);
if (!conn->err_code)
- conn->err_code = CO_ER_SSL_HANDSHAKE;
+ conn->err_code = (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT) ?
+ CO_ER_SSL_KILLED_HB : CO_ER_SSL_HANDSHAKE;
goto out_error;
}
}
@@ -1447,13 +1407,26 @@
if (!errno && conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
- if (!((SSL *)conn->xprt_ctx)->packet_length)
- if (!errno)
- conn->err_code = CO_ER_SSL_EMPTY;
+ if (!((SSL *)conn->xprt_ctx)->packet_length) {
+ if (!errno) {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
+ else
+ conn->err_code = CO_ER_SSL_EMPTY;
+ }
+ else {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
+ else
+ conn->err_code = CO_ER_SSL_ABORT;
+ }
+ }
+ else {
+ if (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT)
+ conn->err_code = CO_ER_SSL_HANDSHAKE_HB;
else
- conn->err_code = CO_ER_SSL_ABORT;
- else
- conn->err_code = CO_ER_SSL_HANDSHAKE;
+ conn->err_code = CO_ER_SSL_HANDSHAKE;
+ }
goto out_error;
}
else {
@@ -1465,7 +1438,8 @@
*/
conn_drain(conn);
if (!conn->err_code)
- conn->err_code = CO_ER_SSL_HANDSHAKE;
+ conn->err_code = (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT) ?
+ CO_ER_SSL_KILLED_HB : CO_ER_SSL_HANDSHAKE;
goto out_error;
}
}
@@ -1897,6 +1871,70 @@
return 1;
}
+char *ssl_sock_get_version(struct connection *conn)
+{
+ if (!ssl_sock_is_ssl(conn))
+ return NULL;
+
+ return (char *)SSL_get_version(conn->xprt_ctx);
+}
+
+/* returns common name, NULL terminated, from client certificate, or NULL if none */
+char *ssl_sock_get_common_name(struct connection *conn)
+{
+ X509 *crt = NULL;
+ X509_NAME *name;
+ struct chunk *cn_trash;
+ const char find_cn[] = "CN";
+ const struct chunk find_cn_chunk = {
+ .str = (char *)&find_cn,
+ .len = sizeof(find_cn)-1
+ };
+ char *result = NULL;
+
+ if (!ssl_sock_is_ssl(conn))
+ return NULL;
+
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (!crt)
+ goto out;
+
+ name = X509_get_subject_name(crt);
+ if (!name)
+ goto out;
+
+ cn_trash = get_trash_chunk();
+ if (ssl_sock_get_dn_entry(name, &find_cn_chunk, 1, cn_trash) <= 0)
+ goto out;
+ cn_trash->str[cn_trash->len] = '\0';
+ result = cn_trash->str;
+
+ out:
+ if (crt)
+ X509_free(crt);
+
+ return result;
+}
+
+/* returns 1 if client passed a certificate, 0 if not */
+int ssl_sock_get_cert_used(struct connection *conn)
+{
+ if (!ssl_sock_is_ssl(conn))
+ return 0;
+
+ return SSL_SOCK_ST_FL_VERIFY_DONE & conn->xprt_st ? 1 : 0;
+}
+
+/* returns result from SSL verify */
+unsigned int ssl_sock_get_verify_result(struct connection *conn)
+{
+ if (!ssl_sock_is_ssl(conn))
+ return (unsigned int)X509_V_ERR_APPLICATION_VERIFICATION;
+
+ return (unsigned int)SSL_get_verify_result(conn->xprt_ctx);
+}
+
/***** Below are some sample fetching functions for ACL/patterns *****/
/* boolean, returns true if client cert was present */
@@ -1925,11 +1963,15 @@
return 1;
}
-/* bin, returns serial in a binary chunk */
+/* binary, returns serial of certificate in a binary chunk.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_serial(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_serial(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
int ret = 0;
struct chunk *smp_trash;
@@ -1947,8 +1989,11 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
+
if (!crt)
goto out;
@@ -1960,16 +2005,21 @@
smp->type = SMP_T_BIN;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
-/* bin, returns the client certificate's SHA-1 fingerprint (SHA-1 hash of DER-encoded certificate) in a binary chunk */
+/* binary, returns the client certificate's SHA-1 fingerprint (SHA-1 hash of DER-encoded certificate) in a binary chunk.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_sha1(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_sha1(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
const EVP_MD *digest;
int ret = 0;
@@ -1988,8 +2038,10 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
goto out;
@@ -2001,16 +2053,21 @@
smp->type = SMP_T_BIN;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
-/*str, returns notafter date in ASN1_UTCTIME format */
+/* string, returns certificate's notafter date in ASN1_UTCTIME format.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_notafter(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_notafter(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
int ret = 0;
struct chunk *smp_trash;
@@ -2028,8 +2085,10 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
goto out;
@@ -2041,16 +2100,21 @@
smp->type = SMP_T_STR;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
-/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+/* string, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. of certificate's issuer
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
X509_NAME *name;
int ret = 0;
@@ -2069,8 +2133,10 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
goto out;
@@ -2097,16 +2163,21 @@
smp->data.str = *smp_trash;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
-/*str, returns notbefore date in ASN1_UTCTIME format */
+/* string, returns notbefore date in ASN1_UTCTIME format.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_notbefore(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_notbefore(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
int ret = 0;
struct chunk *smp_trash;
@@ -2124,8 +2195,10 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
goto out;
@@ -2137,16 +2210,21 @@
smp->type = SMP_T_STR;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
-/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+/* string, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. of certificate's subject
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_s_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_s_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt = NULL;
X509_NAME *name;
int ret = 0;
@@ -2165,8 +2243,10 @@
return 0;
}
- /* SSL_get_peer_certificate, it increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
goto out;
@@ -2193,7 +2273,8 @@
smp->data.str = *smp_trash;
ret = 1;
out:
- if (crt)
+ /* SSL_get_peer_certificate, it increase X509 * ref count */
+ if (cert_peer && crt)
X509_free(crt);
return ret;
}
@@ -2229,11 +2310,15 @@
return 1;
}
-/* integer, returns the client certificate version */
+/* integer, returns the certificate version
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_version(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_version(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt;
struct connection *conn;
@@ -2249,23 +2334,31 @@
return 0;
}
- /* SSL_get_peer_certificate returns a ptr on allocated X509 struct */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
return 0;
smp->data.uint = (unsigned int)(1 + X509_get_version(crt));
- X509_free(crt);
+ /* SSL_get_peer_certificate increase X509 * ref count */
+ if (cert_peer)
+ X509_free(crt);
smp->type = SMP_T_UINT;
return 1;
}
-/* str, returns the client certificate sig alg */
+/* string, returns the certificate's signature algorithm.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_sig_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_sig_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt;
int nid;
struct connection *conn;
@@ -2282,8 +2375,10 @@
return 0;
}
- /* SSL_get_peer_certificate increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
return 0;
@@ -2291,23 +2386,31 @@
smp->data.str.str = (char *)OBJ_nid2sn(nid);
if (!smp->data.str.str) {
- X509_free(crt);
+ /* SSL_get_peer_certificate increase X509 * ref count */
+ if (cert_peer)
+ X509_free(crt);
return 0;
}
smp->type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
smp->data.str.len = strlen(smp->data.str.str);
- X509_free(crt);
+ /* SSL_get_peer_certificate increase X509 * ref count */
+ if (cert_peer)
+ X509_free(crt);
return 1;
}
-/* str, returns the client certificate key alg */
+/* string, returns the certificate's key algorithm.
+ * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
+ * should be use.
+ */
static int
-smp_fetch_ssl_c_key_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_ssl_x_key_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int cert_peer = (kw[4] == 'c') ? 1 : 0;
X509 *crt;
int nid;
struct connection *conn;
@@ -2324,8 +2427,10 @@
return 0;
}
- /* SSL_get_peer_certificate increase X509 * ref count */
- crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ if (cert_peer)
+ crt = SSL_get_peer_certificate(conn->xprt_ctx);
+ else
+ crt = SSL_get_certificate(conn->xprt_ctx);
if (!crt)
return 0;
@@ -2333,24 +2438,31 @@
smp->data.str.str = (char *)OBJ_nid2sn(nid);
if (!smp->data.str.str) {
- X509_free(crt);
+ /* SSL_get_peer_certificate increase X509 * ref count */
+ if (cert_peer)
+ X509_free(crt);
return 0;
}
smp->type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
smp->data.str.len = strlen(smp->data.str.str);
- X509_free(crt);
+ if (cert_peer)
+ X509_free(crt);
return 1;
}
-/* boolean, returns true if front conn. transport layer is SSL */
+/* boolean, returns true if front conn. transport layer is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
- struct connection *conn = objt_conn(l4->si[0].end);
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
+ struct connection *conn = objt_conn(l4->si[back_conn].end);
smp->type = SMP_T_BOOL;
smp->data.uint = (conn && conn->xprt == &ssl_sock);
@@ -2375,333 +2487,15 @@
#endif
}
-/* bin, returns serial in a binary chunk */
-static int
-smp_fetch_ssl_f_serial(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt = NULL;
- int ret = 0;
- struct chunk *smp_trash;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- goto out;
-
- smp_trash = get_trash_chunk();
- if (ssl_sock_get_serial(crt, smp_trash) <= 0)
- goto out;
-
- smp->data.str = *smp_trash;
- smp->type = SMP_T_BIN;
- ret = 1;
-out:
- return ret;
-}
-/*str, returns notafter date in ASN1_UTCTIME format */
-static int
-smp_fetch_ssl_f_notafter(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt = NULL;
- int ret = 0;
- struct chunk *smp_trash;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- goto out;
-
- smp_trash = get_trash_chunk();
- if (ssl_sock_get_time(X509_get_notAfter(crt), smp_trash) <= 0)
- goto out;
-
- smp->data.str = *smp_trash;
- smp->type = SMP_T_STR;
- ret = 1;
-out:
- return ret;
-}
-
-/*str, returns notbefore date in ASN1_UTCTIME format */
-static int
-smp_fetch_ssl_f_notbefore(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt = NULL;
- int ret = 0;
- struct chunk *smp_trash;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- goto out;
-
- smp_trash = get_trash_chunk();
- if (ssl_sock_get_time(X509_get_notBefore(crt), smp_trash) <= 0)
- goto out;
-
- smp->data.str = *smp_trash;
- smp->type = SMP_T_STR;
- ret = 1;
-out:
- return ret;
-}
-
-/* integer, returns the frontend certificate version */
-static int
-smp_fetch_ssl_f_version(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- /* SSL_get_certificate returns a ptr on an SSL * internal sub struct */
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- return 0;
-
- smp->data.uint = (unsigned int)(1 + X509_get_version(crt));
- smp->type = SMP_T_UINT;
-
- return 1;
-}
-
-/* str, returns the client certificate sig alg */
-static int
-smp_fetch_ssl_f_sig_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt;
- int nid;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- return 0;
-
- nid = OBJ_obj2nid((ASN1_OBJECT *)(crt->cert_info->signature->algorithm));
-
- smp->data.str.str = (char *)OBJ_nid2sn(nid);
- if (!smp->data.str.str)
- return 0;
-
- smp->type = SMP_T_STR;
- smp->flags |= SMP_F_CONST;
- smp->data.str.len = strlen(smp->data.str.str);
-
- return 1;
-}
-
-/* str, returns the client certificate key alg */
-static int
-smp_fetch_ssl_f_key_alg(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt;
- int nid;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- return 0;
-
- nid = OBJ_obj2nid((ASN1_OBJECT *)(crt->cert_info->key->algor->algorithm));
-
- smp->data.str.str = (char *)OBJ_nid2sn(nid);
- if (!smp->data.str.str)
- return 0;
-
- smp->type = SMP_T_STR;
- smp->flags |= SMP_F_CONST;
- smp->data.str.len = strlen(smp->data.str.str);
-
- return 1;
-}
-
-/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
-static int
-smp_fetch_ssl_f_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt = NULL;
- X509_NAME *name;
- int ret = 0;
- struct chunk *smp_trash;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- goto out;
-
- name = X509_get_issuer_name(crt);
- if (!name)
- goto out;
-
- smp_trash = get_trash_chunk();
- if (args && args[0].type == ARGT_STR) {
- int pos = 1;
-
- if (args[1].type == ARGT_SINT)
- pos = args[1].data.sint;
- else if (args[1].type == ARGT_UINT)
- pos =(int)args[1].data.uint;
-
- if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
- goto out;
- }
- else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
- goto out;
-
- smp->type = SMP_T_STR;
- smp->data.str = *smp_trash;
- ret = 1;
-out:
- return ret;
-}
-
-/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
-static int
-smp_fetch_ssl_f_s_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *args, struct sample *smp, const char *kw)
-{
- X509 *crt = NULL;
- X509_NAME *name;
- int ret = 0;
- struct chunk *smp_trash;
- struct connection *conn;
-
- if (!l4)
- return 0;
-
- conn = objt_conn(l4->si[0].end);
- if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
- return 0;
-
- if (!(conn->flags & CO_FL_CONNECTED)) {
- smp->flags |= SMP_F_MAY_CHANGE;
- return 0;
- }
-
- crt = SSL_get_certificate(conn->xprt_ctx);
- if (!crt)
- goto out;
-
- name = X509_get_subject_name(crt);
- if (!name)
- goto out;
-
- smp_trash = get_trash_chunk();
- if (args && args[0].type == ARGT_STR) {
- int pos = 1;
-
- if (args[1].type == ARGT_SINT)
- pos = args[1].data.sint;
- else if (args[1].type == ARGT_UINT)
- pos =(int)args[1].data.uint;
-
- if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
- goto out;
- }
- else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
- goto out;
-
- smp->type = SMP_T_STR;
- smp->data.str = *smp_trash;
- ret = 1;
-out:
- return ret;
-}
-
+/* string, returns the used cipher if front conn. transport layer is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc_cipher(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
struct connection *conn;
smp->flags = 0;
@@ -2709,7 +2503,7 @@
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2724,10 +2518,16 @@
return 1;
}
+/* integer, returns the algoritm's keysize if front conn. transport layer
+ * is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc_alg_keysize(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
struct connection *conn;
smp->flags = 0;
@@ -2735,7 +2535,7 @@
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2747,10 +2547,15 @@
return 1;
}
+/* integer, returns the used keysize if front conn. transport layer is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc_use_keysize(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
struct connection *conn;
smp->flags = 0;
@@ -2758,7 +2563,7 @@
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2827,10 +2632,15 @@
}
#endif
+/* string, returns the used protocol if front conn. transport layer is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc_protocol(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
struct connection *conn;
smp->flags = 0;
@@ -2838,7 +2648,7 @@
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2853,11 +2663,16 @@
return 1;
}
+/* binary, returns the SSL session id if front conn. transport layer is SSL.
+ * This function is also usable on backend conn if the fetch keyword 5th
+ * char is 'b'.
+ */
static int
smp_fetch_ssl_fc_session_id(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw)
{
#if OPENSSL_VERSION_NUMBER > 0x0090800fL
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
SSL_SESSION *sess;
struct connection *conn;
@@ -2867,7 +2682,7 @@
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2918,18 +2733,17 @@
const struct arg *args, struct sample *smp, const char *kw)
{
#if OPENSSL_VERSION_NUMBER > 0x0090800fL
+ int back_conn = (kw[4] == 'b') ? 1 : 0;
struct connection *conn;
int finished_len;
- int b64_len;
struct chunk *finished_trash;
- struct chunk *smp_trash;
smp->flags = 0;
if (!l4)
return 0;
- conn = objt_conn(l4->si[0].end);
+ conn = objt_conn(l4->si[back_conn].end);
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
return 0;
@@ -2947,15 +2761,9 @@
if (!finished_len)
return 0;
- smp_trash = get_trash_chunk();
- b64_len = a2base64(finished_trash->str, finished_len, smp_trash->str, smp_trash->size);
- if (b64_len < 0)
- return 0;
-
- smp->data.str.str = smp_trash->str;
- smp->type = SMP_T_STR;
- smp->flags |= SMP_F_CONST;
- smp->data.str.len = b64_len;
+ finished_trash->len = finished_len;
+ smp->data.str = *finished_trash;
+ smp->type = SMP_T_BIN;
return 1;
#else
@@ -3605,6 +3413,22 @@
newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLS_TICKETS;
return 0;
}
+/* parse the "send-proxy-v2-ssl" server keyword */
+static int srv_parse_send_proxy_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+ newsrv->pp_opts |= SRV_PP_V2;
+ newsrv->pp_opts |= SRV_PP_V2_SSL;
+ return 0;
+}
+
+/* parse the "send-proxy-v2-ssl-cn" server keyword */
+static int srv_parse_send_proxy_cn(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+ newsrv->pp_opts |= SRV_PP_V2;
+ newsrv->pp_opts |= SRV_PP_V2_SSL;
+ newsrv->pp_opts |= SRV_PP_V2_SSL_CN;
+ return 0;
+}
/* parse the "ssl" server keyword */
static int srv_parse_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
@@ -3656,28 +3480,36 @@
* Please take care of keeping this list alphabetically sorted.
*/
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
+ { "ssl_bc", smp_fetch_ssl_fc, 0, NULL, SMP_T_BOOL, SMP_USE_L5SRV },
+ { "ssl_bc_alg_keysize", smp_fetch_ssl_fc_alg_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5SRV },
+ { "ssl_bc_cipher", smp_fetch_ssl_fc_cipher, 0, NULL, SMP_T_STR, SMP_USE_L5SRV },
+ { "ssl_bc_protocol", smp_fetch_ssl_fc_protocol, 0, NULL, SMP_T_STR, SMP_USE_L5SRV },
+ { "ssl_bc_unique_id", smp_fetch_ssl_fc_unique_id, 0, NULL, SMP_T_BIN, SMP_USE_L5SRV },
+ { "ssl_bc_use_keysize", smp_fetch_ssl_fc_use_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5SRV },
+ { "ssl_bc_session_id", smp_fetch_ssl_fc_session_id, 0, NULL, SMP_T_BIN, SMP_USE_L5SRV },
{ "ssl_c_ca_err", smp_fetch_ssl_c_ca_err, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
{ "ssl_c_ca_err_depth", smp_fetch_ssl_c_ca_err_depth, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
{ "ssl_c_err", smp_fetch_ssl_c_err, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
- { "ssl_c_i_dn", smp_fetch_ssl_c_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_key_alg", smp_fetch_ssl_c_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_notafter", smp_fetch_ssl_c_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_notbefore", smp_fetch_ssl_c_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_sig_alg", smp_fetch_ssl_c_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_s_dn", smp_fetch_ssl_c_s_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_c_serial", smp_fetch_ssl_c_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
- { "ssl_c_sha1", smp_fetch_ssl_c_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
+ { "ssl_c_i_dn", smp_fetch_ssl_x_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_key_alg", smp_fetch_ssl_x_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_notafter", smp_fetch_ssl_x_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_notbefore", smp_fetch_ssl_x_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_sig_alg", smp_fetch_ssl_x_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_s_dn", smp_fetch_ssl_x_s_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
+ { "ssl_c_sha1", smp_fetch_ssl_x_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_c_used", smp_fetch_ssl_c_used, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
{ "ssl_c_verify", smp_fetch_ssl_c_verify, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
- { "ssl_c_version", smp_fetch_ssl_c_version, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
- { "ssl_f_i_dn", smp_fetch_ssl_f_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_key_alg", smp_fetch_ssl_f_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_notafter", smp_fetch_ssl_f_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_notbefore", smp_fetch_ssl_f_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_sig_alg", smp_fetch_ssl_f_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_s_dn", smp_fetch_ssl_f_s_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_serial", smp_fetch_ssl_f_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
- { "ssl_f_version", smp_fetch_ssl_f_version, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
+ { "ssl_c_version", smp_fetch_ssl_x_version, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
+ { "ssl_f_i_dn", smp_fetch_ssl_x_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_key_alg", smp_fetch_ssl_x_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_notafter", smp_fetch_ssl_x_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_notbefore", smp_fetch_ssl_x_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_sig_alg", smp_fetch_ssl_x_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_s_dn", smp_fetch_ssl_x_s_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
+ { "ssl_f_sha1", smp_fetch_ssl_x_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
+ { "ssl_f_version", smp_fetch_ssl_x_version, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
{ "ssl_fc", smp_fetch_ssl_fc, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
{ "ssl_fc_alg_keysize", smp_fetch_ssl_fc_alg_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
{ "ssl_fc_cipher", smp_fetch_ssl_fc_cipher, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
@@ -3690,7 +3522,7 @@
{ "ssl_fc_alpn", smp_fetch_ssl_fc_alpn, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
#endif
{ "ssl_fc_protocol", smp_fetch_ssl_fc_protocol, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_fc_unique_id", smp_fetch_ssl_fc_unique_id, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_unique_id", smp_fetch_ssl_fc_unique_id, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_fc_use_keysize", smp_fetch_ssl_fc_use_keysize, 0, NULL, SMP_T_UINT, SMP_USE_L5CLI },
{ "ssl_fc_session_id", smp_fetch_ssl_fc_session_id, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_fc_sni", smp_fetch_ssl_fc_sni, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
@@ -3701,29 +3533,6 @@
* Please take care of keeping this list alphabetically sorted.
*/
static struct acl_kw_list acl_kws = {ILH, {
- { "ssl_c_i_dn", NULL, PAT_MATCH_STR },
- { "ssl_c_key_alg", NULL, PAT_MATCH_STR },
- { "ssl_c_notafter", NULL, PAT_MATCH_STR },
- { "ssl_c_notbefore", NULL, PAT_MATCH_STR },
- { "ssl_c_sig_alg", NULL, PAT_MATCH_STR },
- { "ssl_c_s_dn", NULL, PAT_MATCH_STR },
- { "ssl_c_serial", NULL, PAT_MATCH_BIN },
- { "ssl_f_i_dn", NULL, PAT_MATCH_STR },
- { "ssl_f_key_alg", NULL, PAT_MATCH_STR },
- { "ssl_f_notafter", NULL, PAT_MATCH_STR },
- { "ssl_f_notbefore", NULL, PAT_MATCH_STR },
- { "ssl_f_sig_alg", NULL, PAT_MATCH_STR },
- { "ssl_f_s_dn", NULL, PAT_MATCH_STR },
- { "ssl_f_serial", NULL, PAT_MATCH_BIN },
- { "ssl_fc_cipher", NULL, PAT_MATCH_STR },
-#ifdef OPENSSL_NPN_NEGOTIATED
- { "ssl_fc_npn", NULL, PAT_MATCH_STR },
-#endif
-#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
- { "ssl_fc_alpn", NULL, PAT_MATCH_STR },
-#endif
- { "ssl_fc_protocol", NULL, PAT_MATCH_STR },
- { "ssl_fc_sni", "ssl_fc_sni", PAT_MATCH_STR },
{ "ssl_fc_sni_end", "ssl_fc_sni", PAT_MATCH_END },
{ "ssl_fc_sni_reg", "ssl_fc_sni", PAT_MATCH_REG },
{ /* END */ },
@@ -3784,6 +3593,8 @@
{ "no-tlsv11", srv_parse_no_tlsv11, 0, 0 }, /* disable TLSv11 */
{ "no-tlsv12", srv_parse_no_tlsv12, 0, 0 }, /* disable TLSv12 */
{ "no-tls-tickets", srv_parse_no_tls_tickets, 0, 0 }, /* disable session resumption tickets */
+ { "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 0 }, /* send PROXY protocol header v2 with SSL info */
+ { "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 0 }, /* send PROXY protocol header v2 with CN */
{ "ssl", srv_parse_ssl, 0, 0 }, /* enable SSL processing */
{ "verify", srv_parse_verify, 1, 0 }, /* set SSL verify method */
{ "verifyhost", srv_parse_verifyhost, 1, 0 }, /* require that SSL cert verifies for hostname */
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/standard.c
^
|
@@ -635,6 +635,11 @@
* - "ipv6@" => force address to resolve as IPv6 and fail if not possible.
* - "unix@" => force address to be a path to a UNIX socket even if the
* path does not start with a '/'
+ * - 'abns@' -> force address to belong to the abstract namespace (Linux
+ * only). These sockets are just like Unix sockets but without
+ * the need for an underlying file system. The address is a
+ * string. Technically it's like a Unix socket with a zero in
+ * the first byte of the address.
* - "fd@" => an integer must follow, and is a file descriptor number.
*
* Also note that in order to avoid any ambiguity with IPv6 addresses, the ':'
@@ -655,6 +660,7 @@
char *back, *str2;
char *port1, *port2;
int portl, porth, porta;
+ int abstract = 0;
portl = porth = porta = 0;
@@ -668,6 +674,12 @@
if (strncmp(str2, "unix@", 5) == 0) {
str2 += 5;
+ abstract = 0;
+ ss.ss_family = AF_UNIX;
+ }
+ else if (strncmp(str2, "abns@", 5) == 0) {
+ str2 += 5;
+ abstract = 1;
ss.ss_family = AF_UNIX;
}
else if (strncmp(str2, "ipv4@", 5) == 0) {
@@ -706,7 +718,7 @@
/* complete unix socket path name during startup or soft-restart is
* <unix_bind_prefix><path>.<pid>.<bak|tmp>
*/
- prefix_path_len = pfx ? strlen(pfx) : 0;
+ prefix_path_len = (pfx && !abstract) ? strlen(pfx) : 0;
max_path_len = (sizeof(((struct sockaddr_un *)&ss)->sun_path) - 1) -
(prefix_path_len ? prefix_path_len + 1 + 5 + 1 + 3 : 0);
@@ -716,9 +728,11 @@
goto out;
}
+ /* when abstract==1, we skip the first zero and copy all bytes except the trailing zero */
+ memset(((struct sockaddr_un *)&ss)->sun_path, 0, sizeof(((struct sockaddr_un *)&ss)->sun_path));
if (prefix_path_len)
memcpy(((struct sockaddr_un *)&ss)->sun_path, pfx, prefix_path_len);
- memcpy(((struct sockaddr_un *)&ss)->sun_path + prefix_path_len, str2, adr_len + 1);
+ memcpy(((struct sockaddr_un *)&ss)->sun_path + prefix_path_len + abstract, str2, adr_len + 1 - abstract);
}
else { /* IPv4 and IPv6 */
port1 = strrchr(str2, ':');
|
[-]
[+]
|
Changed |
_service:download_url:haproxy-1.5-dev25.tar.gz/src/stream_interface.c
^
|
@@ -422,11 +422,7 @@
if (conn->data == &si_conn_cb) {
struct stream_interface *si = conn->owner;
struct connection *remote = objt_conn(si->ob->prod->end);
-
- if (remote)
- ret = make_proxy_line(trash.str, trash.size, &remote->addr.from, &remote->addr.to);
- else
- ret = make_proxy_line(trash.str, trash.size, NULL, NULL);
+ ret = make_proxy_line(trash.str, trash.size, objt_server(conn->target), remote);
}
else {
/* The target server expects a LOCAL line to be sent first. Retrieving
@@ -440,7 +436,7 @@
if (!(conn->flags & CO_FL_ADDR_TO_SET))
goto out_wait;
- ret = make_proxy_line(trash.str, trash.size, &conn->addr.from, &conn->addr.to);
+ ret = make_proxy_line(trash.str, trash.size, objt_server(conn->target), conn);
}
if (!ret)
|