commit 85b77ed0114b5a6cdae2ee91a9209edeb03b6009
parent 00b9a457e57ed25bd9a9edcc5bc4c9d8bb0db5d4
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:15:07 +0100
1.23
Diffstat:
136 files changed, 6932 insertions(+), 2705 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,6 +1,46 @@
ChangeLog for Crossroads
------------------------------------------------------------------------------
+1.23 [KK 2007-01-04] Removed "sampleconf" program option. There are
+ too many config options to have all the sample text in the
+ binary. Other small fixes.
+
+1.22 [KK 2006-12-21] Implemented username directive.
+ [KK 2006-12-22] Fixed counting of total sessions.
+
+1.21 [KK 2006-12-11] Implemented externalhandler as dispatch mode.
+ Added a few sample handlers, updated the docs. NOTE: The external
+ dispatcher handlers are ALPHA CODE at this time.
+ [KK 2006-12-13] Changed the 'configuration' section of the
+ docs. Directives are now in their own subsection, so that they
+ have their own TOC entry.
+ [KK 2006-12-14] Changed the 'onsuccess' directive to
+ 'onstart'. Implemented 'onend'. Commands that are run via these
+ hooks, plus via 'onfailure', get expanded. Added format expanders
+ '%n' (connections of a back end) and '%w' (weights of a back
+ end). Thanks Bernd Krumboeck for loads of suggestions, code,
+ diffs and testing!
+ [KK 2006-12-19] Compilation fixes, thanks again Bernd.
+
+1.20 [KK 2006-12-06] Updated connection handler in HTTP requests to
+ show the right title (which backend is being served). Fixed bug
+ in client connection counting. Fixed status reporter to show an
+ 's' after the display of seconds.
+ [KK 2006-12-08] Fixed status reporter to use suffix Tb (tera) or
+ Gb (giga) for large bytecounts.
+
+1.19 [KK 2006-12-04] Implemented IP filtering (configuration:
+ allowfrom/allowfile, denyfrom/denyfile). Updated docs. Recoded
+ filtering so that the order is "deny,allow" (a-la Apache). More
+ messaging in "dispatchmode roundrobin".
+
+1.18 [KK 2006-11-29] Next development version. Fixed net_copy() to
+ correctly decrement the client connection count (thanks Bernd
+ Krumboeck for the fix!). Small document fix.
+ [KK 2006-12-01] http_header_connectiontype() will now correctly
+ understand Connection-Type: TE, close (ie. when the "close" is
+ part of a larger setting).
+
1.17 [KK 2006-11-28] Next development version.
[KK 2006-11-29] Removed debugging stuff which by mistake slipped
into 1.16 stable. Shipping this 1.17 as next-stable (instead of
diff --git a/Makefile b/Makefile
@@ -28,5 +28,8 @@ dist: documentation
tools/e-ver ChangeLog $(VER)
tools/makedist $(VER)
-commit: clean documentation
- svn commit
-\ No newline at end of file
+commit: clean headers documentation local clean
+ svn commit
+
+headers:
+ tools/patch-header etc/hdr.template $(VER) src/*.c src/*.h
diff --git a/doc/conf/addclientheader.yo b/doc/conf/addclientheader.yo
@@ -0,0 +1,142 @@
+conf(HTTP Header Modification Directives)
+ (Crossroads understands the following
+ header modification directives: tt(addclientheader),
+ tt(appendclientheader), tt(setclientheader), tt(addserverheader),
+ tt(appendserverheader), tt(setserverheader).
+
+ The directive names always consist of
+ em(Action)em(Destination)tt(header), where:
+
+ itemization(
+ it() The action is tt(add), tt(append) or tt(insert).
+
+ itemization(
+ it() Action tt(add) adds a header, even when headers with
+ the same name already are present in an HTTP
+ message. Adding headers is useful for e.g. tt(Set-Cookie)
+ headers; a message may contain several of such headers.
+
+ it() Action tt(append) adds a header if it isn't present
+ yet in an HTTP message. If such a header is already
+ present, then the value is appended to the pre-existing
+ header. This is useful for e.g. tt(Via) headers. Imagine
+ an HTTP message with a header tt(Via: someproxy). Then the
+ directive tt(appendclientheader "Via: crossroads") will
+ rewrite the header to tt(Via: someproxy; crossroads).
+
+ it() Action tt(set) overwrites headers with the same
+ name; or adds a new header if no pre-existing is found.
+ This is useful for e.g. tt(Host) headers.)
+
+ it() The destination is one of tt(client) or tt(server). When
+ the destination is tt(server), then Crossroads will apply such
+ directives to HTTP messages that originate from the browser
+ and are being forwarded to back ends. When the destination is
+ tt(client), then Crossroads will apply such directives to
+ backend responses that are shuttled to the browser.)
+
+ The format of the directives is e.g. tt(addclientheader
+ "X-Processed-By: Crossroads"). The directives expect one
+ argument; a string, consisting of a header name, a colon, and a
+ header value. As usual, the directive must end with a semicolon.
+
+ The header value may contain one of the following formatting
+ directives:
+
+ INCLUDEFILE(formattable.yo)
+
+ The following examples show common uses of header modifications.
+
+ description(
+ dit(Enforcing session stickiness:) By combining
+ tt(stickycookie) and tt(addclientheader), HTTP session
+ stickiness is enforced. Consider the following configuration:
+
+ verb(\
+service ... {
+ ...
+ backend one {
+ ...
+ addclientheader "Set-Cookie: BalancerID=first; path=/";
+ stickycookie "BalancerID=first";
+ }
+ backend two {
+ ...
+ addclientheader "Set-Cookie: BalancerID=second; path=/";
+ stickycookie "BalancerID=second";
+ }
+})
+
+ The first request of an HTTP session is balanced to either
+ backend tt(one) or tt(two). The server response is enriched
+ using tt(addclientheader) with an appropriate cookie. A
+ subsequent request from the same browser now has that cookie
+ in place; and is therefore sent to the same back end where the
+ its predecessors went.
+
+ dit(Hiding the server software version:) Many servers
+ (e.g. Apache) advertize their version, as in tt(Server: Apache
+ 1.27). This potentially provides information to attackers. The
+ following configuration hides such information:
+
+ verb(\
+service ... {
+ ...
+ backend one {
+ ...
+ setclientheader "Server: WWW-Server";
+ }
+})
+
+ dit(Informing the server of the clients' IP address:) Since
+ Crossroads sits 'in the middle' between a client and a back
+ end, the back end perceives Crossroads as its client. The
+ following sends the true clients' IP address to the server, in
+ a header tt(X-Real-IP):
+
+ verb(\
+service ... {
+ ...
+ backend one {
+ ...
+ setserverheader "X-Real-IP: %r";
+ }
+})
+
+ dit(Keep-Alive Downgrading:) The directives
+ tt(setclientheader) and tt(setserverheader) also play a key
+ role in downgrading Keep-Alive connections to
+ 'single-shot'. E.g., the following configuration makes sure
+ that no Keep-Alive connections occur.
+
+ verb(\
+service ... {
+ ...
+ backend one {
+ ...
+ setserverheader "Connection: close";
+ setclientheader "Connection: close";
+ }
+})))
+ (itemization(
+ it() tt(addclientheader) em(Headername: headervalue) to add a
+ header in the traffic towards the client, even when another
+ header em(Headername) exists;
+ it() tt(appendclientheader) em(Headername: headervalue) to
+ append em(headervalue) to an existing header em(Headername)
+ in the traffic towards the client,
+ or to add the whole header alltogether;
+ it() tt(setclientheader) em(Headername: headervalue) to
+ overwrite an existing header in the traffic towards the
+ client, or to add such a header;
+ it() tt(addserverheader) em(Headername: headervalue) to add a
+ header in the traffic towards the server, even when another
+ header em(Headername) exists;
+ it() tt(appendserverheader) em(Headername: headervalue) to
+ append em(headervalue) to an existing header em(Headername)
+ in the traffic towards the server,
+ or to add the whole header alltogether;
+ it() tt(setserverheader) em(Headername: headervalue) to
+ overwrite an existing header in the traffic towards the
+ server, or to add such a header.))
+ (There is no default.)
diff --git a/doc/conf/allow.yo b/doc/conf/allow.yo
@@ -0,0 +1,65 @@
+conf(allow* and deny* - Allowing or denying connections)
+ (Crossroads can allow or deny
+ connections based on the IP address of a client. There are four
+ directives that are relevant: tt(allowfrom), tt(allowfile),
+ tt(denyfrom) and tt(denyfile). When using tt(allowfrom) and
+ tt(denyfrom) then the IP addresses to allow or deny connections are
+ stated in tt(/etc/crossroads.conf).
+
+ When tt(allow*) directives are used, then all connections are denied
+ unless they match the stated allowed IP's. When tt(deny*) directives
+ are used, then all connections are allowed unless they match the
+ stated disallowed IP's. When denying and allowing is both used,
+ then the Crossroads checks the deny list first.
+
+ The statements tt(allowfrom) and tt(denyfrom) are followed by a
+ list of filter specifications. The statements tt(allowfile) and
+ tt(denyfile) are followed by a filename; Crossroads will read
+ filter specifications from those external files. In both cases,
+ Crossroads obtains filter specifications and places them in its
+ lists of allowed or denied IP addresses. The difference between
+ specifying filters in tt(/etc/crossroads.conf) or in external
+ files, is that Crossroads will reload the external files when it
+ receives signal 1 (tt(SIGHUP)), as in tt(killall -1 crossroads).
+
+ The filter specifications must obey the following syntax: it
+ consists of up to
+ four numbers ranging from 0 to 255 and separated by a decimal
+ sign. Optionally a slash follows, with a bitmask which is also a
+ decimal number.
+
+ This is probably best explained by a few examples:
+
+ itemization(
+ it() tt(allowfrom 10/8;) will allow connections from
+ tt(10.*.*.*) (a full Class A network). The mask tt(/8) means
+ that the first 8 bits of the number (ie., only the tt(10)) are
+ significant. On the last 3 positions of the IP address, all
+ numbers are allowed. Given this directive, client connections
+ from e.g. 10.1.1.1 and 10.2.3.4 will be allowed.
+
+ it() tt(allowfrom 10.3/16;) will allow all IP addresses that
+ start with tt(10.3).
+
+ it() tt(allowfrom 10.3.1/16;) is the same as above. The third
+ byte of the IP address is superfluous because the netmask
+ specifies that only the first 16 bits (2 numbers) are taken
+ into account.
+
+ it() tt(allowfrom 10.3.1.15;) allows traffic from only the
+ specified IP address. There is no bitmask; all four numbers
+ are relevant.
+
+ it() tt(allowfrom 10.3.1.15 10.2/16;) allows traffic from one
+ IP address tt(10.3.1.15) or from a complete Class B network
+ tt(10.2.*.*)
+
+ it() tt(allowfile /tmp/myfile.txt;) in combination with a file
+ tt(/tmp/myfile.txt), with the contents tt(10.3.1.15 10.2/16),
+ is the same as above.))
+ (itemization(
+ it() tt(allowfrom) em(filter-specificication(s))
+ it() tt(denyfrom) em(filter-specificication(s))
+ it() tt(allowfile) em(filename)
+ it() tt(denyfile) em(filename)))
+ (In absence of these statements, all client IP's are accepted.)
diff --git a/doc/conf/backlog.yo b/doc/conf/backlog.yo
@@ -0,0 +1,12 @@
+conf(backlog - The TCP Back Log size)
+ (The TCP back log size is a number that controls how many
+ 'waiting' network connections may be queued, before a client simply
+ cannot connect. The syntax is e.g. tt(backlog 5) to cause crossroads
+ to have 5 waiting connections for 1 active connection.
+ The backlog queue shouldn't be too
+ high, or clients will experience timeouts before they can actually
+ connect. The queue shouldn't be too small either, because clients
+ would be simply rejected. Your mileage may vary.)
+ (tt(backlog) em(number))
+ (0, which takes the operating system's default
+ value for socket back log size.)
diff --git a/doc/conf/bindto.yo b/doc/conf/bindto.yo
@@ -0,0 +1,11 @@
+conf(bindto - Binding to a specific IP address)
+ (The tt(bindto) statement is used in situations where crossroads
+ should only listen to the stated port at a given IP address. E.g.,
+ tt(bindto 127.0.0.1) causes crossroads to 'bind' the service only to
+ the local IP address. Network connections from other hosts won't be
+ serviced. By default, crossroads binds a service to all presently
+ active IP addresses at the invoking host.)
+ (tt(bindto) em(address), where em(address) is a numeric IP
+ address, such as 127.0.0.1, or the keyword tt(any).)
+ (tt(any))
+
+\ No newline at end of file
diff --git a/doc/conf/connectiontimeout.yo b/doc/conf/connectiontimeout.yo
@@ -0,0 +1,11 @@
+conf(connectiontimeout - Specifying client time outs)
+ (Sometimes, clients simply won't close a network
+ connection which leads to unnecessary resource usage. To avoid this,
+ one might state e.g. tt(connectiontimeout 300). This instructs
+ crossroads to consider a connection where nothing has happened for
+ 300 seconds as
+ 'finished'. Crossroads will terminate the connection when this timeout
+ is exceeded.)
+ (tt(connectiontimeout) em(number), where em(number) specifies the
+ timeout in seconds.)
+ (0, meaning that crossroads will not cut connections.)
diff --git a/doc/conf/decay.yo b/doc/conf/decay.yo
@@ -0,0 +1,18 @@
+conf(decay - Levelling out activity of a back end)
+ (To make sure that a 'spike' of activity doesn't
+ influence the perceived load of a back end forever, you may
+ specify a certain decay. E.g, the statement tt(decay 10) makes
+ sure that the load that crossroads computes for this back end (be
+ it in seconds or in bytes) is decreased by 10% each time that
+ bf(an other) back end is hit. Decays are not applied to the count
+ of concurrent connections.
+
+ This means that when a given back end is hit, then its usage data
+ of the transferred bytes and the connection duration are updated
+ using the actual number of bytes and actual duration. However,
+ when a different back end is hit, then the usage data are
+ decreased by the specified decay. )
+ (tt(decay) em(number), where em(number) is a percentage that
+ decreases the back end usage data when other back ends are
+ hit.)
+ (0, meaning that no decay is applied to usage statistics.)
diff --git a/doc/conf/dispatchmode.yo b/doc/conf/dispatchmode.yo
@@ -0,0 +1,78 @@
+conf(dispatchmode - How are back ends selected)
+ (The dispatch mode controls how crossroads selects a back end from
+ a list of active back ends. The below text shows the bare
+ syntax. See section ref(howselected) for a textual explanation.
+
+ The settings can be:
+
+ itemization(
+ it() tt(dispatchmode roundrobin): Simply the 'next in line' is
+ chosen. E.g, when 3 back ends are active, then the usage
+ series is 1, 2, 3, 1, 2, 3, and so on.
+
+ Roundrobin dispatching is the default method, when no
+ tt(dispatchmode) statement occurs.
+
+ it() tt(dispatchmode random): Random selection. Probably only
+ for stress testing, though when used with weights (see below)
+ it is a good distributor of new connections too.
+
+ it() tt(dispatchmode bysize [ over) em(connections) tt(]):
+ The next back end is the one
+ that has transferred the least number of bytes. This
+ selection mechanism assumes that the more bytes, the heavier
+ the load.
+
+ The modifier tt(over) em(connections) is optional. (The square
+ brackets shown above are not part of the statement but
+ indicate optionality.) When given,
+ the load is computed as an average of the last stated number of
+ connections. When this modifier is absent, then the load is
+ computed over all connections since startup.
+
+ it() tt(dispatchmode byduration [ over) em(connections) tt(]):
+ The next back end is the one
+ that served connections for the shortest time. This mechanism
+ assumes that the longer the connection, the heavier the load.
+
+ it() tt(dispatchmode byconnections): The next back end is the one
+ with the least active connections. This mechanism assumes that
+ each connection to a back end represents load. It is usable
+ for e.g. database connections.
+
+ it() tt(dispatchmode byorder): The first back end is selected
+ every time, unless it's unavailable. In that case the second
+ is taken, and so on.
+
+ it() tt(dispatchmode externalhandler) em(program arguments):
+ This is a special mode, where an external program is delegated
+ the responsibility to say which back end should be used
+ next. In this case, Crossroads will call the external program,
+ and this will of course be slower than one of the 'built-in'
+ dispatch modes. However, this is the ultimate escape when
+ custom-made dispatch modes are needed.
+
+ The dispatch mode that uses an tt(externalhandler) is
+ discussed separately in section ref(externalhandler).)
+
+ The selection algorithm is only used when clients are serviced that
+ aren't part of a sticky HTTP session. This is the case during:
+
+ itemization(
+ it() all client requests of a service type tt(any);
+ it() new sessions of a service type tt(http).)
+
+ When type tt(http) is in effect and a session is underway, then the
+ previously used back end is always selected -- regardless of
+ dispatching mode.
+
+ Your 'right' dispatch mode will depend on the type of service. Given
+ the fact that crossroads doesn't know (and doesn't care) how to
+ estimate load from a network traffic stream, you have to choose an
+ appropriate dispatch mode to optimize load balancing. In most cases,
+ tt(roundrobin) or tt(byconnections) will do the job just fine.)
+ (tt(dispatchmode) em(mode) (see above for the modes), optionally
+ followed by tt(over) em(number), or when the em(mode) is
+ tt(externalhandler), followed by em(program).)
+ (tt(roundrobin))
+
diff --git a/doc/conf/maxconnections.yo b/doc/conf/maxconnections.yo
@@ -0,0 +1,12 @@
+conf(maxconnections - Limiting concurrent clients at service level)
+ (The maximum number of connections is specified using
+ tt(maxconnections). There is one argument; the number of concurrent
+ established connections that may be active within one service.
+
+ 'Throttling' the number of connections is a way of preventing Denial of
+ Service (DOS) attacks. Without a limit, numerous network connections
+ may spawn so many server instances, that the service ultimately breaks
+ down and becomes unavailable.)
+ (tt(maxconnections) em(number), where the number specifies the
+ maximum of concurrent connections to the service.)
+ (0, meaning that all connections will be accepted.)
diff --git a/doc/conf/onhooks.yo b/doc/conf/onhooks.yo
@@ -0,0 +1,25 @@
+conf(onstart, onend, onfail - Action Hooks)
+ (The three directives tt(onstart), tt(onend) and tt(onfail) can be
+ specified to start system commands (external programs) when a
+ connection to a back end starts, fails or ends:
+ itemization(
+ it() tt(onstart) commands will be run when Crossroads
+ successfully connects to a back end, and starts servicing;
+ it() tt(onend) commands will be run when a (previously
+ established) connection stops;
+ it() tt(onfail) commands will be run when Crossroads tries to
+ contact a back end to serve a client, but the back end can't
+ be reached.)
+
+ The format is always tt(on)em(type) em(command). The em(command)
+ is an external program, optionally followed by arguments. The
+ command is expanded according to the following table:
+
+ INCLUDEFILE(formattable))
+ (itemization(
+ it() tt(onstart) em(commandline)
+ it() tt(onend) em(commandline)
+ it() tt(onfail) em(commandline)
+ it() tt(onsuccess) em(commandline)))
+ (There is no default. Normally no external programs are run upon
+ connection, success or failure of a back end.)
diff --git a/doc/conf/port.yo b/doc/conf/port.yo
@@ -0,0 +1,7 @@
+conf(port - Specifying the listen port)
+ (The tt(port) statement defines to which TCP port a service
+ 'listens'. E.g. tt(port 8000) says that this service will accept
+ connections on port 8000.)
+ (tt(port) em(number))
+ (There is no default. This is a required setting.)
+
+\ No newline at end of file
diff --git a/doc/conf/revivinginterval.yo b/doc/conf/revivinginterval.yo
@@ -0,0 +1,18 @@
+conf(revivinginterval - Back end wakeup calls)
+ (A reviving interval definition is needed when crossroads
+ determines that a back end is temporarily unavailable. This will
+ happen when:
+
+ itemization(
+ it() The back end cannot be reached (network connection
+ fails);
+ it() The network connection to the back end suddenly dies.)
+
+ An example of the definition is tt(revivinginterval 10). When this
+ reviving interval is given, crossroads will check each 10 seconds
+ whether unavailable back ends have woken up yet. A back end is
+ considered awake when a network connection to that back end can
+ succesfully be established.)
+ (tt(revivinginterval) em(number), where the number is the interval
+ in seconds.)
+ (0 (no wakeup calls))
diff --git a/doc/conf/server.yo b/doc/conf/server.yo
@@ -0,0 +1,10 @@
+conf(server - Specifying the back end address)
+ (Each back end must be identified by the network name
+ (server name) where it is located. For example: tt(server
+ 10.1.1.23), or tt(server web.mydomain.org). A TCP port specifier
+ can follow the server name, as in tt(server web.mydomain.org:80).)
+ (itemization(
+ it() tt(server) em(servername), where em(servername) is a
+ network name or IP address;
+ it() tt(server) em(servername:port)))
+ (There is no default. This is a required setting.)
diff --git a/doc/conf/shmkey.yo b/doc/conf/shmkey.yo
@@ -0,0 +1,17 @@
+conf(shmkey - Shared Memory Access)
+ (Different Crossroads
+ invocations must 'know' of each others activity. E.g, tt(crossroad
+ status) must be able to get to the actual state information of all
+ running services. This is internally implemented through shared
+ memory, which is reserved using a key.
+
+ Normally crossroads will supply a shared memory key, based on the
+ service port and bitwise or-ed with a magic number. In situations
+ where this conflicts with existing keys (of other programs, having
+ their own keys), you may supply a chosen value.
+
+ The actual key value doesn't matter much, as long as it's unique
+ and as long as each invocation of crossroads uses it.)
+ (tt(shmkey) em(number))
+ (0, which means that crossroads will 'guess' its
+ own key, based on TCP port and a magic number.)
diff --git a/doc/conf/stickycookie.yo b/doc/conf/stickycookie.yo
@@ -0,0 +1,35 @@
+conf(stickycookie - Back end selection with an HTTP cookie)
+ (The directive tt(stickycookie) em(value)
+ causes Crossroads to unpack clients' requests, to check for
+ em(value) in the cookies. When found, the message is routed to the
+ back end having the appropriate tt(stickycookie) directive.
+
+ E.g., consider the following configuration:
+
+ verb(\
+service ... {
+ ...
+ backend one {
+ ...
+ stickycookie "BalancerID=first";
+ }
+ backend two {
+ ...
+ stickycookie "BalancerID=second";
+ }
+})
+
+ When clients' messages contain cookies named tt(BalancerID) with
+ the value tt(first), then such messages are routed to backend
+ tt(one). When the value is tt(second) then they are routed to the
+ backend tt(two).
+
+ There are basically to provide such cookies to a browser. First, a
+ back end can insert such a cookie into the HTTP response. E.g.,
+ the webserver of back end tt(one) might insert a cookie named
+ tt(BalancerID), having value tt(first).
+ Second, Crossroads can insert such cookies using a carefully
+ crafted directive tt(addclientheader).)
+ (tt(stickycookie) em(cookievalue))
+ (There is no default.)
+
diff --git a/doc/conf/trafficlog.yo b/doc/conf/trafficlog.yo
@@ -0,0 +1,17 @@
+conf(trafficlog and throughputlog - Debugging and Performance Aids)
+ (Two directives are available
+ to log network traffic to files. They are tt(trafficlog) and
+ tt(throughputlog).
+
+ The tt(trafficlog) statement causes all traffic to be logged in
+ hexadecimal format. Each line is prefixed by tt(B) or tt(C),
+ depending on whether the information was received from the back
+ end or from the client.
+
+ The tt(throughputlog) statement writes shorthand transmissions to
+ its log, accompanied by timings.)
+ (itemization(
+ it() tt(trafficlog) em(filename)
+ it() tt(throughputlog) em(filename)))
+ (none)
+
diff --git a/doc/conf/type.yo b/doc/conf/type.yo
@@ -0,0 +1,18 @@
+conf(type - Defining the service type)
+ (The tt(type) statement defines how crossroads handles the stated
+ service. There are currently two types: tt(any) and
+ tt(http). The type tt(any) means that crossroads doesn't
+ interpret the contents of a TCP stream, but only distributes streams
+ over back ends. The type tt(http) means that crossroads has to
+ analyze what's in the messages, does magical HTTP header tricks, and
+ so on -- all to ensure that multiple connections are treated as one
+ session, or that the back end is notified of the client's IP
+ address.
+
+ Unless you really need such special features, use the type tt(any) (the
+ default), even for HTTP protocols.)
+ (tt(type) em(specifier), where em(specifier) is tt(any) or
+ tt(http))
+ (tt(any))
+
+
diff --git a/doc/conf/useraccount.yo b/doc/conf/useraccount.yo
@@ -0,0 +1,14 @@
+conf(useraccount - Limiting the effective ID of external processes)
+ (Using the directive tt(useraccount), the effective user and group
+ ID can be restricted. This comes into effect when Crossroads runs
+ external commands, such as:
+ itemization(
+ it() Hooks for tt(onstart), tt(onend) or tt(onfail);
+ it() External dispatchers, when tt(dispatchmode
+ externalhandler) is in effect.)
+ Once a user name for external commands is specified, Crossroads
+ assumes the associated user ID and group ID before running those
+ commands.)
+ (tt(useraccount) em(username))
+ (None; when unspecified, external commands are run with the
+ ID that was in effect when Crossroads was started.)
+\ No newline at end of file
diff --git a/doc/conf/verbose-backend.yo b/doc/conf/verbose-backend.yo
@@ -0,0 +1,10 @@
+conf(verbosity - Controlling verbosity at the back end level)
+ (Similar to tt(service) specifications, a
+ tt(backend) can have its own verbosity (tt(on) or tt(off)). When
+ tt(on), traffic to and fro this back end is reported.)
+ (itemization(
+ it() tt(verbosity) em(setting), or
+ it() tt(verbose) em(setting), where em(setting) is tt(true),
+ tt(yes) or tt(on), or tt(false), tt(no), tt(off) to turn it
+ off.))
+ (tt(off))
diff --git a/doc/conf/verbose.yo b/doc/conf/verbose.yo
@@ -0,0 +1,14 @@
+conf(verbosity - Controlling debug output)
+ (Verbosity statements come in two forms: tt(verbosity on) or
+ tt(verbosity off). When 'on', log messages to tt(/var/log/messages)
+ are generated that show what's going on.footnote(Actually, the
+ messages go to tt(syslog(3)), using facility tt(LOG_DAEMON) and
+ priority tt(LOG_INFO). In most (Linux) cases this will mean: output to
+ tt(/var/log/messages). On Mac OSX the messages go to
+ tt(/var/log/system.log).) The keyword tt(verbose) is an alias for
+ tt(verbosity).)
+ (tt(verbosity) em(setting) or tt(verbose) em(setting), where
+ em(setting) is tt(true), tt(yes) or tt(on) to turn
+ verbosity on; or tt(false), tt(no), tt(off) to turn it off.)
+ (tt(off))
+
+\ No newline at end of file
diff --git a/doc/conf/weight.yo b/doc/conf/weight.yo
@@ -0,0 +1,15 @@
+conf(weight - When a back end is more equal than others)
+ (To influence how backends are selected, a backend can specify its
+ 'weight' in the process. The higher the weight, the less likely a
+ back end will be chosen. The default is 1.
+
+ The weighing mechanism only applies to the dispatch modes
+ tt(random), tt(byconnections), tt(bysize) and tt(byduration).
+ The weight is in fact a penalty factor. E.g., if backend A has
+ tt(weight 2) and backend B has tt(weight 1), then backend B will
+ be selected all the time, until its usage parameter is twice as
+ large as the parameter of A. Think of it as a 'sluggishness'
+ statement.)
+ (tt(weight) em(number); the higher the number, the more 'sluggish'
+ a back end is)
+ (1; all back ends have equal weight.)
diff --git a/doc/config.yo b/doc/config.yo
@@ -64,7 +64,7 @@ tt(true), tt(yes) and tt(on) all mean the same and can be used
interchangeably; as can the keywords tt(false), tt(no) and tt(off).
-subsect(Service definitions)
+subsect(Service definitions) label(servicedef)
Service definitions are blocks in the configuration file that
state what is for each service. A service definition starts with
@@ -85,204 +85,26 @@ identifying names differ. The following list shows possible
statements. Each statement must end with a semicolon, except for the
tt(backend) statement, which has is own block (more on this later).
-startdit()
+redef(conf)(4)(\
+ subsubsect(ARG1) label(confARG1)
+ startdit()
+ dit(Description:) ARG2
+ dit(Syntax:) ARG3
+ dit(Default:) ARG4
+ enddit())
-dit(The type statement) defines how crossroads handles the stated
-service. There are currently two types: tt(any) and
-tt(http). The type tt(any) means that crossroads doesn't
-interpret the contents of a TCP stream, but only distributes streams
-over back ends. The type tt(http) means that crossroads has to
-analyze what's in the messages, does magical HTTP header tricks, and
-so on -- all to ensure that multiple connections are treated as one
-session, or that the back end is notified of the client's IP address.
-Unless you really need such special features, use the type tt(any) (the
-default), even for HTTP protocols.
-
-itemization(
- it() Syntax: tt(type) em(typespecifier) tt(;)
- it() Where em(typespecifier) is tt(any) or tt(http)
- it() Default: tt(any))
-
-dit(The port statement) defines to which TCP port a service
-'listens'. E.g. tt(port 8000) says that this service will accept
-connections on port 8000.
-
-itemization(
- it() Syntax: tt(port) em(number) tt(;)
- it() There is no default. This is a required setting.)
-
-dit(The bindto statement) is used in situations where crossroads
-should only listen to the stated port at a given IP address. E.g.,
-tt(bindto 127.0.0.1) causes crossroads to 'bind' the service only to
-the local IP address. Network connections from other hosts won't be
-serviced. By default, crossroads binds a service to all presently
-active IP addresses at the invoking host.
-
-itemization(
- it() Syntax: tt(bindto) em(ip-address) tt(;)
- it() where em(ip-address) is a numeric IP address, such as
- tt(192.168.1.45), or the keyword tt(any)
- it() Default: tt(any))
-
-dit(Verbosity statements) come in two forms: tt(verbosity on) or
-tt(verbosity off). When 'on', log messages to tt(/var/log/messages)
-are generated that show what's going on.footnote(Actually, the
-messages go to tt(syslog(3)), using facility tt(LOG_DAEMON) and
-priority tt(LOG_INFO). In most (Linux) cases this will mean: output to
-tt(/var/log/messages). On Mac OSX the messages go to
-tt(/var/log/system.log).) The keyword tt(verbose) is an alias for
-tt(verbosity).
-
-itemization(
- it() Syntax: tt(verbosity) em(setting) tt(;)
- it() Or: tt(verbose) em(setting) tt(;)
- it() where em(setting) is tt(true), tt(yes) or tt(on) to turn
- verbosity on; or tt(false), tt(no), tt(off) to turn it off.
- it() Default: tt(off).)
-
-dit(The dispatch mode) controls how crossroads selects a back end from
-a list of active back ends. The below text shows the bare
-syntax. See section ref(howselected) for a textual explanation.
-
-The syntax is:
-
-itemization(
- it() tt(dispatchmode roundrobin): Simply the 'next in line' is
- chosen. E.g, when 3 back ends are active, then the usage
- series is 1, 2, 3, 1, 2, 3, and so on.
-
- Roundrobin dispatching is the default method, when no
- tt(dispatchmode) statement occurs.
-
- it() tt(dispatchmode random): Random selection. Probably only
- for stress testing, though when used with weights (see below)
- it is a good distributor of new connections too.
-
- it() tt(dispatchmode bysize [ over) em(connections) tt(]):
- The next back end is the one
- that has transferred the least number of bytes. This
- selection mechanism assumes that the more bytes, the heavier
- the load.
-
- The modifier tt(over) em(connections) is optional. (The square
- brackets shown above are not part of the statement but
- indicate optionality.) When given,
- the load is computed as an average of the last stated number of
- connections. When this modifier is absent, then the load is
- computed over all connections since startup.
-
- it() tt(dispatchmode byduration [ over) em(connections) tt(]):
- The next back end is the one
- that served connections for the shortest time. This mechanism
- assumes that the longer the connection, the heavier the load.
-
- it() tt(dispatchmode byconnections): The next back end is the one
- with the least active connections. This mechanism assumes that
- each connection to a back end represents load. It is usable
- for e.g. database connections.
-
- it() tt(dispatchmode byorder): The first back end is selected
- every time, unless it's unavailable. In that case the second
- is taken, and so on.)
-
-The selection algorithm is only used when clients are serviced that
-aren't part of a sticky HTTP session. This is the case during:
-
-itemization(
- it() all client requests of a service type tt(any);
- it() new sessions of a service type tt(http).)
-
-When type tt(http) is in effect and a session is underway, then the
-previously used back end is always selected -- regardless of
-dispatching mode.
-
-Your 'right' dispatch mode will depend on the type of service. Given
-the fact that crossroads doesn't know (and doesn't care) how to
-estimate load from a network traffic stream, you have to choose an
-appropriate dispatch mode to optimize load balancing. In most cases,
-tt(roundrobin) or tt(byconnections) will do the job just fine.
-
-dit(A reviving interval definition) is needed when crossroads
-determines that a back end is temporarily unavailable. This will
-happen when:
-
-itemization(
- it() The back end cannot be reached (network connection
- fails);
- it() The network connection to the back end suddenly dies.)
-
-An example of the definition is tt(revivinginterval 10). When this
-reviving interval is given, crossroads will check each 10 seconds
-whether unavailable back ends have woken up yet. A back end is
-considered awake when a network connection to that back end can
-succesfully be established.
-
-itemization(
- it() Syntax: tt(revivinginterval) em(number) tt(;)
- it() Default: 0, meaning no revivals will occur.)
-
-dit(The maximum number of connections) is specified using
-tt(maxconnections). There is one argument; the number of concurrent
-established connections that may be active within one service.
-
-'Throttling' the number of connections is a way of preventing Denial of
-Service (DOS) attacks. Without a limit, numerous network connections
-may spawn so many server instances, that the service ultimately breaks
-down and becomes unavailable.
-
-itemization(
- it() Syntax: tt(maxconnections) em(number) tt(;)
- it() Default: 0, meaning that all client connections will be
- accepted.)
-
-dit(The TCP back log size) is a number that controls how many
-'waiting' network connections may be queued, before a client simply
-cannot connect. The syntax is e.g. tt(backlog 5) to cause crossroads
-to have 5 waiting connections for 1 active connection.
-The backlog queue shouldn't be too
-high, or clients will experience timeouts before they can actually
-connect. The queue shouldn't be too small either, because clients
-would be simply rejected. Your mileage may vary.
-
-itemization(
- it() Syntax: tt(backlog) em(number) tt(;)
- it() Default: zero, which takes the operating system's default
- value for socket back log size.)
-
-dit(Reporting based: the shared memory key.) Different crossroad
-invocations must 'know' of each others activity. E.g, tt(crossroad
-status) must be able to get to the actual state information of all
-running services. This is internally implemented through shared
-memory, which is reserved using a key.
-
-Normally crossroads will supply a shared memory key, based on the
-service port and bitwise or-ed with a magic number. In situations
-where this conflicts with existing keys (of other programs, having
-their own keys), you may supply a chosen value.
-
-The syntax is e.g. tt(shmkey 123456). The actual key value doesn't
-matter much, as long as it's unique and as long as each invocation of
-crossroads uses it.
-
-itemization(
- it() Syntax: tt(shmkey) em(number) tt(;)
- it() Default: 0, which means that crossroads will 'guess' its
- own key, based on TCP port and a magic number.)
-
-dit(Connection timeouts:) Sometimes, clients simply won't close a network
-connection which leads to unnecessary resource usage. To avoid this,
-one might state e.g. tt(connectiontimeout 300). This instructs crossroads to
-consider a connection where nothing has happened for 300 seconds as
-'finished'. Crossroads will terminate the connection when this timeout
-is exceeded.
-
-itemization(
- it() Syntax: tt(connectiontimeout) em(number) tt(;)
- it() Default: 0, meaning that crossroads will not try to
- determine timeouts.)
-
-enddit()
+includefile(conf/type)
+includefile(conf/port)
+includefile(conf/bindto)
+includefile(conf/verbose)
+includefile(conf/dispatchmode)
+includefile(conf/revivinginterval)
+includefile(conf/maxconnections)
+includefile(conf/backlog)
+includefile(conf/shmkey)
+includefile(conf/allow)
+includefile(conf/useraccount)
subsect(Backend definitions)
@@ -313,300 +135,16 @@ balancing and fail over) as long as the backend names differ.
The statements in the backend definition blocks are described in the
following sections.
-subsubsect(General Backend Directives)
-
-The following directives are used in all types of services (tt(any) or
-tt(http)). HTTP-specific directives are shown in section ref(httpdirectives).
-
-startdit()
-
- dit(Server:) Each back end must be identified by the network name
- (server name) where it is located. For example: tt(server
- 10.1.1.23), or tt(server web.mydomain.org). A TCP port specifier
- can follow the server name, as in tt(server web.mydomain.org:80).
-
- itemization(
- it() Syntax: tt(server) em(servername) tt(;)
- it() Or: tt(server) em(servername)tt(:)em(port) tt(;)
- it() There is no default. This is a required setting.)
-
- dit(Port:) When the tt(server) specifier doesn't include a TCP
- port, then this statement is used to define the port at which the
- back end expects its traffic. There is one argument, the (numeric)
- port number.
-
- itemization(
- it() Syntax: tt(port) em(number) tt(;)
- it() There is no default. The port must be defined either in
- the tt(server) setting or using the tt(port) specifier.)
-
- dit(Verbosity:) Similar to tt(service) specifications, a
- tt(backend) can have its own verbosity (tt(on) or tt(off)). When
- tt(on), traffic to and fro this back end is reported.
-
- itemization(
- it() Syntax: tt(verbosity) em(setting) tt(;)
- it() Or: tt(verbose) em(setting) tt(;)
- it() where em(setting) is tt(true), tt(yes) or tt(on) to turn
- verbosity on; or tt(false), tt(no), tt(off) to turn it off.
- it() Default: tt(off).)
-
- dit(Maxconnections:) This setting states how many concurrent connections
- a back end connection may accept. Note that there is also a
- tt(maxconnections) statement for the overall service description.
-
- The difference is that a tt(maxconnections) statement at the level of
- a service description avoids too many hits from the outside (DOS
- prevention). A tt(maxconnections) statement at the level of a back end
- description makes sure that this particular back end doesn't get
- overloaded.
-
- itemization(
- it() Syntax: tt(maxconnections) em(number) tt(;)
- it() where em(number) is the maximum number of concurrent
- client connections.
- it() Default: 0, meaning that there is no limit.)
-
- dit(Weight:) To influence how backends are selected by size or by
- duration, a backend can specify its 'weight' in the process. The
- higher the weight, the less likely a back end will be chosen. The
- default is 1.
-
- The weighing mechanism only applies to the dispatch modes
- tt(random), tt(byconnections), tt(bysize) and tt(byduration).
- The weight is in fact a penalty factor. E.g., if backend A has
- tt(weight 2) and backend B has tt(weight 1), then backend B will
- be selected all the time, until its usage parameter is twice as
- large as the parameter of A. Think of it as a 'sluggishness' statement.
-
- itemization(
- it() Syntax: tt(weight) em(number) tt(;)
- it() Default: 1)
-
- dit(Decay:) To make sure that a 'spike' of activity doesn't
- influence the perceived load of a back end forever, you may
- specify a certain decay. E.g, the statement tt(decay 10) makes
- sure that the load that crossroads computes for this back end (be
- it in seconds or in bytes) is decreased by 10% each time that
- bf(an other) back end is hit. Decays are not applied to the count
- of concurrent connections.
-
- This means that when a given back end is hit, then its usage data
- of the transferred bytes and the connection duration are updated
- using the actual number of bytes and actual duration. However,
- when a different back end is hit, then the usage data are
- decreased by the specified decay.
-
- itemization(
- it() Syntax: tt(decay) em(number) tt(;)
- it() where em(number) is a percentage that decreases the back
- data when other back ends are hit
- it() Default: 0, meaning that no decay is applied to usage
- statistics.)
-
- dit(Event triggers:) As special 'hooks' for actions, two triggers
- are available: tt(onfailure) and tt(onsuccess). The argument to
- the triggers is a system command that is executed when a connection
- with the back end either fails or succeeds.
-
- itemization(
- it() Syntax: tt(onfailure) em(commandline) tt(;) and
- tt(onsuccess) em(commandline) tt(;)
- it() There is no default.)
-
- dit(Debugging and Performance aids:) Two directives are available
- to log network traffic to files. They are tt(trafficlog) and
- tt(throughputlog).
-
- itemization(
- it() Syntax: tt(trafficlog) em(filename) tt(;)
- it() And: tt(throughputlog) em(filename) tt(;)
- it() Default: none)
-
- The tt(trafficlog) statement causes all traffic to be logged in
- hexadecimal format. Each line is prefixed by tt(B) or tt(C),
- depending on whether the information was received from the back
- end or from the client.
-
- The tt(throughputlog) statement writes shorthand transmissions to
- its log, accompanied by timings.
-
-enddit()
-
-subsubsect(HTTP-related Backend Directives) label(httpdirectives)
-
-The following directives are specific for HTTP-type services; i.e.,
-services with a specification tt(type http).
-
-It is inevitable that when Crossroads handles services of tt(type
-http), more processing is necessary. Crossroads has to unpack the TCP
-payload in order to do its header magic; which leads to performance
-impact.
-
-startdit()
-
- dit(Session Stickiness:) The directive tt(stickycookie) em(value)
- causes Crossroads to unpack clients' requests, to check for
- em(value) in the cookies. When found, the message is routed to the
- back end having the appropriate tt(stickycookie) directive.
-
- E.g., consider the following configuration:
-
- verb(\
-service ... {
- ...
- backend one {
- ...
- stickycookie "BalancerID=first";
- }
- backend two {
- ...
- stickycookie "BalancerID=second";
- }
-})
-
- When clients' messages contain cookies named tt(BalancerID) with
- the value tt(first), then such messages are routed to backend
- tt(one). When the value is tt(second) then they are routed to the
- backend tt(two).
-
- There are basically to provide such cookies to a browser. First, a
- back end can insert such a cookie into the HTTP response. E.g.,
- the webserver of back end tt(one) might insert a cookie named
- tt(BalancerID), having value tt(first).
- Second, Crossroads can insert such cookies using a carefully
- crafted directive tt(addclientheader). See below.
-
- dit(Header modification:) Crossroads understands the following
- header modification directives: tt(addclientheader),
- tt(appendclientheader), tt(setclientheader), tt(addserverheader),
- tt(appendserverheader), tt(setserverheader).
-
- The directive names always consist of
- em(Action)em(Destination)tt(header), where:
-
- itemization(
- it() The action is tt(add), tt(append) or tt(insert).
-
- itemization(
- it() Action tt(add) adds a header, even when headers with
- the same name already are present in an HTTP
- message. Adding headers is useful for e.g. tt(Set-Cookie)
- headers; a message may contain several of such headers.
-
- it() Action tt(append) adds a header if it isn't present
- yet in an HTTP message. If such a header is already
- present, then the value is appended to the pre-existing
- header. This is useful for e.g. tt(Via) headers. Imagine
- an HTTP message with a header tt(Via: someproxy). Then the
- directive tt(appendclientheader "Via: crossroads") will
- rewrite the header to tt(Via: someproxy; crossroads).
-
- it() Action tt(set) overwrites headers with the same
- name; or adds a new header if no pre-existing is found.
- This is useful for e.g. tt(Host) headers.)
-
- it() The destination is one of tt(client) or tt(server). When
- the destination is tt(server), then Crossroads will apply such
- directives to HTTP messages that originate from the browser
- and are being forwarded to back ends. When the destination is
- tt(client), then Crossroads will apply such directives to
- backend responses that are shuttled to the browser.)
-
- The syntax of the directives is e.g. tt(addclientheader
- "X-Processed-By: Crossroads";). The directives expect one
- argument; a string, consisting of a header name, a colon, and a
- header value. The directive ends with a semicolon.
-
- The header value may contain one of the following formatting
- directives:
-
- itemization(
- it() tt(%r) is expanded to the real IP address of a client;
- it() tt(%t) is expanded to a timestamp of the local time;
- it() tt(%T) is expanded to a timestamp of Greenwich Mean Time;
- it() tt(%v) is expanded to the Crossroads version;
- it() tt(%)em(x) (where em(x) is any other character) is
- expanded to em(x). E.g., tt(%%) is a literal % sign.)
-
- dit(Common Uses)
-
- The following examples show common uses of header modifications.
-
- description(
- dit(Enforcing session stickiness:) By combining
- tt(stickycookie) and tt(addclientheader), HTTP session
- stickiness is enforced. Consider the following configuration:
-
- verb(\
-service ... {
- ...
- backend one {
- ...
- addclientheader "Set-Cookie: BalancerID=first; path=/";
- stickycookie "BalancerID=first";
- }
- backend two {
- ...
- addclientheader "Set-Cookie: BalancerID=second; path=/";
- stickycookie "BalancerID=second";
- }
-})
-
- The first request of an HTTP session is balanced to either
- backend tt(one) or tt(two). The server response is enriched
- using tt(addclientheader) with an appropriate cookie. A
- subsequent request from the same browser now has that cookie
- in place; and is therefore sent to the same back end where the
- its predecessors went.
-
- dit(Hiding the server software version:) Many servers
- (e.g. Apache) advertize their version, as in tt(Server: Apache
- 1.27). This potentially provides information to attackers. The
- following configuration hides such information:
-
- verb(\
-service ... {
- ...
- backend one {
- ...
- setclientheader "Server: WWW-Server";
- }
-})
-
- dit(Informing the server of the clients' IP address:) Since
- Crossroads sits 'in the middle' between a client and a back
- end, the back end perceives Crossroads as its client. The
- following sends the true clients' IP address to the server, in
- a header tt(X-Real-IP):
-
- verb(\
-service ... {
- ...
- backend one {
- ...
- setserverheader "X-Real-IP: %r";
- }
-})
-
- dit(Keep-Alive Downgrading:) The directives
- tt(setclientheader) and tt(setserverheader) also play a key
- role in downgrading Keep-Alive connections to
- 'single-shot'. E.g., the following configuration makes sure
- that no Keep-Alive connections occur.
-
- verb(\
-service ... {
- ...
- backend one {
- ...
- setserverheader "Connection: close";
- setclientheader "Connection: close";
- }
-})
-
- )
-
-enddit()
-
-
+Some directives (tt(stickycookie) etc.) only have effect when
+Crossroads treats the network traffic as a stream of HTTP messages;
+i.e., when the service is declared with tt(type http). Incase of
+tt(type any), the HTTP-specific directives have no effect.
+
+includefile(conf/server.yo)
+includefile(conf/verbose-backend.yo)
+includefile(conf/weight)
+includefile(conf/decay)
+includefile(conf/onhooks)
+includefile(conf/trafficlog)
+includefile(conf/stickycookie)
+includefile(conf/addclientheader)
diff --git a/doc/crossroads.html b/doc/crossroads.html
@@ -1,12 +1,12 @@
<a name="defs.yo"></a><html><head>
-<title>Crossroads 1.17</title>
+<title>Crossroads 1.23</title>
<link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css">
<link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css">
<link rev="made" href="mailto:info@e-tunity.com">
</head>
<body>
<hr>
-<h1>Crossroads 1.17</h1>
+<h1>Crossroads 1.23</h1>
<h2>Karel Kubat</h2>
<h2>e-tunity</h2><h2>2005, 2006, ff.</h2>
@@ -31,79 +31,112 @@
<dt><a href="#l2">1.1: Obtaining Crossroads</a></dt>
<dt><a href="#l3">1.2: Copyright and Disclaimer</a></dt>
<dt><a href="#l4">1.3: Terminology</a></dt>
-<dt><a href="#l5">1.4: Porting issues for pre-0.26 installations</a></dt>
-<dt><a href="#l6">1.5: Porting issues for pre-1.08 installations</a></dt>
+<dt><a href="#l5">1.4: Porting issues for pre-1.21 installations</a></dt>
+<dt><a href="#l6">1.5: Porting issues for pre-0.26 installations</a></dt>
+<dt><a href="#l7">1.6: Porting issues for pre-1.08 installations</a></dt>
</dl>
-<dt><h3><a href="#l7">2: Installation for the impatient</a></h3></dt>
-<dt><h3><a href="#l8">3: Using Crossroads</a></h3></dt>
+<dt><h3><a href="#l8">2: Installation for the impatient</a></h3></dt>
+<dt><h3><a href="#l9">3: Using Crossroads</a></h3></dt>
<dl>
-<dt><a href="#l9">3.1: General Commandline Syntax</a></dt>
-<dt><a href="#l10">3.2: Logging-related options</a></dt>
+<dt><a href="#l10">3.1: General Commandline Syntax</a></dt>
+<dt><a href="#l11">3.2: Logging-related options</a></dt>
+<dt><a href="#l12">3.3: Reloading Configurations</a></dt>
</dl>
-<dt><h3><a href="#l11">4: The configuration</a></h3></dt>
+<dt><h3><a href="#l13">4: The configuration</a></h3></dt>
<dl>
-<dt><a href="#l12">4.1: General language elements</a></dt>
+<dt><a href="#l14">4.1: General language elements</a></dt>
<dl>
-<dt><a href="#l13">4.1.1: Empty lines and comments</a></dt>
-<dt><a href="#l14">4.1.2: Keywords, numbers, identifiers, generic strings</a></dt>
+<dt><a href="#l15">4.1.1: Empty lines and comments</a></dt>
+<dt><a href="#l16">4.1.2: Keywords, numbers, identifiers, generic strings</a></dt>
</dl>
-<dt><a href="#l15">4.2: Service definitions</a></dt>
-<dt><a href="#l16">4.3: Backend definitions</a></dt>
+<dt><a href="#l17">4.2: Service definitions</a></dt>
<dl>
-<dt><a href="#l17">4.3.1: General Backend Directives</a></dt>
-<dt><a href="#l18">4.3.2: HTTP-related Backend Directives</a></dt>
+<dt><a href="#l18">4.2.1: type - Defining the service type</a></dt>
+<dt><a href="#l19">4.2.2: port - Specifying the listen port</a></dt>
+<dt><a href="#l20">4.2.3: bindto - Binding to a specific IP address</a></dt>
+<dt><a href="#l21">4.2.4: verbosity - Controlling debug output</a></dt>
+<dt><a href="#l22">4.2.5: dispatchmode - How are back ends selected</a></dt>
+<dt><a href="#l23">4.2.6: revivinginterval - Back end wakeup calls</a></dt>
+<dt><a href="#l24">4.2.7: maxconnections - Limiting concurrent clients at service level</a></dt>
+<dt><a href="#l25">4.2.8: backlog - The TCP Back Log size</a></dt>
+<dt><a href="#l26">4.2.9: shmkey - Shared Memory Access</a></dt>
+<dt><a href="#l27">4.2.10: allow* and deny* - Allowing or denying connections</a></dt>
+<dt><a href="#l28">4.2.11: useraccount - Limiting the effective ID of external processes</a></dt>
</dl>
+<dt><a href="#l29">4.3: Backend definitions</a></dt>
+<dl>
+<dt><a href="#l30">4.3.1: server - Specifying the back end address</a></dt>
+<dt><a href="#l31">4.3.2: verbosity - Controlling verbosity at the back end level</a></dt>
+<dt><a href="#l32">4.3.3: weight - When a back end is more equal than others</a></dt>
+<dt><a href="#l33">4.3.4: decay - Levelling out activity of a back end</a></dt>
+<dt><a href="#l34">4.3.5: onstart, onend, onfail - Action Hooks</a></dt>
+<dt><a href="#l35">4.3.6: trafficlog and throughputlog - Debugging and Performance Aids</a></dt>
+<dt><a href="#l36">4.3.7: stickycookie - Back end selection with an HTTP cookie</a></dt>
+<dt><a href="#l37">4.3.8: HTTP Header Modification Directives</a></dt>
+</dl>
+</dl>
+<dt><h3><a href="#l38">5: Tips, Tricks and Remarks</a></h3></dt>
+<dl>
+<dt><a href="#l39">5.1: How back ends are selected in load balancing</a></dt>
+<dl>
+<dt><a href="#l40">5.1.1: Bysize, byduration or byconnections?</a></dt>
+<dt><a href="#l41">5.1.2: Averaging size and duration</a></dt>
+<dt><a href="#l42">5.1.3: Specifying decays</a></dt>
+<dt><a href="#l43">5.1.4: Adjusting the weights</a></dt>
+<dt><a href="#l44">5.1.5: Throttling the number of concurrent connections</a></dt>
</dl>
-<dt><h3><a href="#l19">5: Tips, Tricks and Remarks</a></h3></dt>
+<dt><a href="#l45">5.2: Using an external program to dispatch</a></dt>
<dl>
-<dt><a href="#l20">5.1: How back ends are selected in load balancing</a></dt>
+<dt><a href="#l46">5.2.1: Configuring the external handler</a></dt>
+<dt><a href="#l47">5.2.2: Writing the external handler</a></dt>
+<dt><a href="#l48">5.2.3: Examples of external handlers</a></dt>
+</dl>
+<dt><a href="#l49">5.3: HTTP Session Stickiness</a></dt>
<dl>
-<dt><a href="#l21">5.1.1: Bysize, byduration or byconnections?</a></dt>
-<dt><a href="#l22">5.1.2: Averaging size and duration</a></dt>
-<dt><a href="#l23">5.1.3: Specifying decays</a></dt>
-<dt><a href="#l24">5.1.4: Adjusting the weights</a></dt>
-<dt><a href="#l25">5.1.5: Throttling the number of concurrent connections</a></dt>
+<dt><a href="#l50">5.3.1: Don't use stickiness!</a></dt>
+<dt><a href="#l51">5.3.2: But if you must..</a></dt>
</dl>
-<dt><a href="#l26">5.2: HTTP Session Stickiness</a></dt>
+<dt><a href="#l52">5.4: Passing the client's IP address</a></dt>
<dl>
-<dt><a href="#l27">5.2.1: Don't use stickiness!</a></dt>
-<dt><a href="#l28">5.2.2: But if you must..</a></dt>
+<dt><a href="#l53">5.4.1: Sample Crossroads configuration</a></dt>
+<dt><a href="#l54">5.4.2: Sample Apache configuration</a></dt>
</dl>
-<dt><a href="#l29">5.3: Passing the client's IP address</a></dt>
+<dt><a href="#l55">5.5: Debugging network traffic</a></dt>
+<dt><a href="#l56">5.6: Limiting Access to Crossroads by Client IP Address</a></dt>
<dl>
-<dt><a href="#l30">5.3.1: Sample Crossroads configuration</a></dt>
-<dt><a href="#l31">5.3.2: Sample Apache configuration</a></dt>
+<dt><a href="#l57">5.6.1: General Examples</a></dt>
+<dt><a href="#l58">5.6.2: Using External Files</a></dt>
+<dt><a href="#l59">5.6.3: Mixing Directives</a></dt>
</dl>
-<dt><a href="#l32">5.4: Debugging network traffic</a></dt>
-<dt><a href="#l33">5.5: Configuration examples</a></dt>
+<dt><a href="#l60">5.7: Configuration examples</a></dt>
<dl>
-<dt><a href="#l34">5.5.1: A load balancer for three webserver back ends</a></dt>
-<dt><a href="#l35">5.5.2: An HTTP forwarder when travelling</a></dt>
-<dt><a href="#l36">5.5.3: SSH login with enforced idle logout</a></dt>
+<dt><a href="#l61">5.7.1: A load balancer for three webserver back ends</a></dt>
+<dt><a href="#l62">5.7.2: An HTTP forwarder when travelling</a></dt>
+<dt><a href="#l63">5.7.3: SSH login with enforced idle logout</a></dt>
</dl>
</dl>
-<dt><h3><a href="#l37">6: Benchmarking</a></h3></dt>
+<dt><h3><a href="#l64">6: Benchmarking</a></h3></dt>
<dl>
-<dt><a href="#l38">6.1: Benchmark 1: Accessing a proxy via crossroads or directly</a></dt>
+<dt><a href="#l65">6.1: Benchmark 1: Accessing a proxy via crossroads or directly</a></dt>
<dl>
-<dt><a href="#l39">6.1.1: Results</a></dt>
-<dt><a href="#l40">6.1.2: Discussion</a></dt>
+<dt><a href="#l66">6.1.1: Results</a></dt>
+<dt><a href="#l67">6.1.2: Discussion</a></dt>
</dl>
-<dt><a href="#l41">6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</a></dt>
+<dt><a href="#l68">6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</a></dt>
<dl>
-<dt><a href="#l42">6.2.1: Environment</a></dt>
-<dt><a href="#l43">6.2.2: Tests and results</a></dt>
+<dt><a href="#l69">6.2.1: Environment</a></dt>
+<dt><a href="#l70">6.2.2: Tests and results</a></dt>
</dl>
</dl>
-<dt><h3><a href="#l44">7: Compiling and Installing</a></h3></dt>
+<dt><h3><a href="#l71">7: Compiling and Installing</a></h3></dt>
<dl>
-<dt><a href="#l45">7.1: Prerequisites</a></dt>
-<dt><a href="#l46">7.2: Compiling and installing</a></dt>
-<dt><a href="#l47">7.3: Configuring crossroads</a></dt>
-<dt><a href="#l48">7.4: A boot script</a></dt>
+<dt><a href="#l72">7.1: Prerequisites</a></dt>
+<dt><a href="#l73">7.2: Compiling and installing</a></dt>
+<dt><a href="#l74">7.3: Configuring crossroads</a></dt>
+<dt><a href="#l75">7.4: A boot script</a></dt>
<dl>
-<dt><a href="#l49">7.4.1: SysV Style Startup</a></dt>
-<dt><a href="#l50">7.4.2: BSD Style Startup</a></dt>
+<dt><a href="#l76">7.4.1: SysV Style Startup</a></dt>
+<dt><a href="#l77">7.4.2: BSD Style Startup</a></dt>
</dl>
<p><hr><p>
@@ -269,7 +302,22 @@ using the terms here in a very strict sense.)
</dl>
<p>
<a name="l5"></a>
-<h3>1.4: Porting issues for pre-0.26 installations</h3>
+<h3>1.4: Porting issues for pre-1.21 installations</h3>
+<p>
+As of version 1.21, the event-hook directives <code>onsuccess</code> and
+ <code>onfailure</code> no longer exists.
+<p>
+<ul>
+ <li> Please replace <code>onsuccess</code> by <code>onstart</code>;
+ <li> Please replace <code>onfailure</code> bu <code>onfail</code>;
+ <li> Note that there is a new hook <code>onend</code>.</ul>
+<p>
+The commands that are run via <code>onstart</code>, <code>onend</code> or <code>onfail</code>
+ are subject to format expansion; e.g., <code>%1w</code> is expanded to the
+ weight of the first back end, etc.. See section <a href="crossroads.html#config">4</a> for details.
+<p>
+<a name="l6"></a>
+<h3>1.5: Porting issues for pre-0.26 installations</h3>
<p>
As of version 0.26 the syntax of the configuration file has
changed. In particular:
@@ -287,8 +335,8 @@ Therefore when converting configuration files to the new syntax,
<em>session</em> is used strictly in that sense -- and no longer for a
TCP connection.)
<p>
-<a name="l6"></a>
-<h3>1.5: Porting issues for pre-1.08 installations</h3>
+<a name="l7"></a>
+<h3>1.6: Porting issues for pre-1.08 installations</h3>
<p>
As of version 1.08, the following directives no longer are
supported:
@@ -308,7 +356,7 @@ As of version 1.08, the following directives no longer are
This incidentally also makes it possible to change the header
name (here: <code>XR-Real-IP</code>).</ul>
<p>
-<a name="l7"></a>
+<a name="l8"></a>
<h2>2: Installation for the impatient</h2>
<a name="impatient"></a>
For the impatient, here's the very-quick-but-very-superficial recipy
@@ -378,7 +426,7 @@ That's off course assuming that you want to balance HTTP on
status</code>.
</ul>
<p>
-<a name="l8"></a>
+<a name="l9"></a>
<h2>3: Using Crossroads</h2>
<a name="using"></a>Crossroads is started from the commandline, and highly depends on
<code>/etc/crossroads.conf</code> (the default configuration file). It
@@ -387,7 +435,7 @@ configuration file). The actual usage information is always obtained
by typing <code>crossroads</code> without any arguments. Crossroads then
displays the allowed arguments.
<p>
-<a name="l9"></a>
+<a name="l10"></a>
<h3>3.1: General Commandline Syntax</h3>
<p>
This section shows the most basic usage. As said above, start
@@ -423,7 +471,7 @@ This section shows the most basic usage. As said above, start
configuration <code>/etc/crossroads.conf</code>.
</ul>
<p>
-<a name="l10"></a>
+<a name="l11"></a>
<h3>3.2: Logging-related options</h3>
<p>
Two 'flags' of Crossroads are specifically logging-related. This
@@ -464,9 +512,20 @@ That instructs <code>syslogd</code> to send <code>LOG_LOCAL7</code> requests to
<li> Now start <code>crossroads</code> with the flag <code>-l7</code>.
<p>
<li> Finally, monitor <code>/var/log/crossroads.log</code> for Crossroads'
- messages.</ul>
+ messages.</ul>
<p>
-<a name="l11"></a>
+<a name="l12"></a>
+<h3>3.3: Reloading Configurations</h3>
+<p>
+Crossroads doesn't support the reloading of a configuration while
+running (such as other programs, e.g. Apache do). There are various
+technical reasons for this.
+<p>
+However, external lists of allowed or denied IP addresses can be
+reloaded by sending a signal -1 (<code>SIGHUP</code>) to Crossroads. See
+section <a href="crossroads.html#servicedef">4.2</a> for the details.
+<p>
+<a name="l13"></a>
<h2>4: The configuration</h2>
<a name="config"></a>The configuration that crossroads uses is normally stored in the file
<code>/etc/crossroads.conf</code>. This location can be overruled using the
@@ -475,13 +534,13 @@ command line flag <code>-c</code>.
This section explains the syntax of the configuration file, and what
all settings do.
<p>
-<a name="l12"></a>
+<a name="l14"></a>
<h3>4.1: General language elements</h3>
<p>
This section describes the general elements of the crossroads
configuration language.
<p>
-<a name="l13"></a>
+<a name="l15"></a>
<strong>4.1.1: Empty lines and comments</strong>
<p>
Empty lines are of course allowed in the
@@ -499,7 +558,7 @@ best'. (I favor C or C++ comment. My favorite editor <em>emacs</em>
can be put in <code>cmode</code> and nicely highlight what's comment and what's
not. And as a bonus it will auto-indent the configuration!)
<p>
-<a name="l14"></a>
+<a name="l16"></a>
<strong>4.1.2: Keywords, numbers, identifiers, generic strings</strong>
<p>
In a configuration file, statements are identified by <em>keywords</em>,
@@ -534,8 +593,8 @@ Finally, an argument can be a 'boolean' value. Crossroads knows
<code>true</code>, <code>yes</code> and <code>on</code> all mean the same and can be used
interchangeably; as can the keywords <code>false</code>, <code>no</code> and <code>off</code>.
<p>
-<a name="l15"></a>
-<h3>4.2: Service definitions</h3>
+<a name="l17"></a>
+<h3>4.2: Service definitions</h3> <a name="servicedef"></a>
<p>
Service definitions are blocks in the configuration file that
state what is for each service. A service definition starts with
@@ -558,67 +617,75 @@ identifying names differ. The following list shows possible
statements. Each statement must end with a semicolon, except for the
<code>backend</code> statement, which has is own block (more on this later).
<p>
-<dl>
-<p>
-<p><dt><strong>The type statement</strong><dd> defines how crossroads handles the stated
-service. There are currently two types: <code>any</code> and
-<code>http</code>. The type <code>any</code> means that crossroads doesn't
-interpret the contents of a TCP stream, but only distributes streams
-over back ends. The type <code>http</code> means that crossroads has to
-analyze what's in the messages, does magical HTTP header tricks, and
-so on -- all to ensure that multiple connections are treated as one
-session, or that the back end is notified of the client's IP address.
+<a name="conf/type"></a><a name="l18"></a>
+<strong>4.2.1: type - Defining the service type</strong> <a name="conftype - Defining the service type"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The <code>type</code> statement defines how crossroads handles the stated
+ service. There are currently two types: <code>any</code> and
+ <code>http</code>. The type <code>any</code> means that crossroads doesn't
+ interpret the contents of a TCP stream, but only distributes streams
+ over back ends. The type <code>http</code> means that crossroads has to
+ analyze what's in the messages, does magical HTTP header tricks, and
+ so on -- all to ensure that multiple connections are treated as one
+ session, or that the back end is notified of the client's IP
+ address.
<p>
Unless you really need such special features, use the type <code>any</code> (the
-default), even for HTTP protocols.
-<p>
-<ul>
- <li> Syntax: <code>type</code> <em>typespecifier</em> <code>;</code>
- <li> Where <em>typespecifier</em> is <code>any</code> or <code>http</code>
- <li> Default: <code>any</code></ul>
-<p>
-<p><dt><strong>The port statement</strong><dd> defines to which TCP port a service
-'listens'. E.g. <code>port 8000</code> says that this service will accept
-connections on port 8000.
-<p>
-<ul>
- <li> Syntax: <code>port</code> <em>number</em> <code>;</code>
- <li> There is no default. This is a required setting.</ul>
-<p>
-<p><dt><strong>The bindto statement</strong><dd> is used in situations where crossroads
-should only listen to the stated port at a given IP address. E.g.,
-<code>bindto 127.0.0.1</code> causes crossroads to 'bind' the service only to
-the local IP address. Network connections from other hosts won't be
-serviced. By default, crossroads binds a service to all presently
-active IP addresses at the invoking host.
-<p>
-<ul>
- <li> Syntax: <code>bindto</code> <em>ip-address</em> <code>;</code>
- <li> where <em>ip-address</em> is a numeric IP address, such as
- <code>192.168.1.45</code>, or the keyword <code>any</code>
- <li> Default: <code>any</code></ul>
-<p>
-<p><dt><strong>Verbosity statements</strong><dd> come in two forms: <code>verbosity on</code> or
-<code>verbosity off</code>. When 'on', log messages to <code>/var/log/messages</code>
-are generated that show what's going on. (Actually, the
-messages go to <code>syslog(3)</code>, using facility <code>LOG_DAEMON</code> and
-priority <code>LOG_INFO</code>. In most (Linux) cases this will mean: output to
-<code>/var/log/messages</code>. On Mac OSX the messages go to
-<code>/var/log/system.log</code>.) The keyword <code>verbose</code> is an alias for
-<code>verbosity</code>.
-<p>
-<ul>
- <li> Syntax: <code>verbosity</code> <em>setting</em> <code>;</code>
- <li> Or: <code>verbose</code> <em>setting</em> <code>;</code>
- <li> where <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn
- verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off.
- <li> Default: <code>off</code>.</ul>
-<p>
-<p><dt><strong>The dispatch mode</strong><dd> controls how crossroads selects a back end from
-a list of active back ends. The below text shows the bare
-syntax. See section <a href="crossroads.html#howselected">5.1</a> for a textual explanation.
-<p>
-The syntax is:
+ default), even for HTTP protocols.
+ <p><dt><strong>Syntax:</strong><dd> <code>type</code> <em>specifier</em>, where <em>specifier</em> is <code>any</code> or
+ <code>http</code>
+ <p><dt><strong>Default:</strong><dd> <code>any</code>
+ </dl>
+<p>
+<a name="conf/port"></a><a name="l19"></a>
+<strong>4.2.2: port - Specifying the listen port</strong> <a name="confport - Specifying the listen port"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The <code>port</code> statement defines to which TCP port a service
+ 'listens'. E.g. <code>port 8000</code> says that this service will accept
+ connections on port 8000.
+ <p><dt><strong>Syntax:</strong><dd> <code>port</code> <em>number</em>
+ <p><dt><strong>Default:</strong><dd> There is no default. This is a required setting.
+ </dl>
+<p>
+<a name="conf/bindto"></a><a name="l20"></a>
+<strong>4.2.3: bindto - Binding to a specific IP address</strong> <a name="confbindto - Binding to a specific IP address"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The <code>bindto</code> statement is used in situations where crossroads
+ should only listen to the stated port at a given IP address. E.g.,
+ <code>bindto 127.0.0.1</code> causes crossroads to 'bind' the service only to
+ the local IP address. Network connections from other hosts won't be
+ serviced. By default, crossroads binds a service to all presently
+ active IP addresses at the invoking host.
+ <p><dt><strong>Syntax:</strong><dd> <code>bindto</code> <em>address</em>, where <em>address</em> is a numeric IP
+ address, such as 127.0.0.1, or the keyword <code>any</code>.
+ <p><dt><strong>Default:</strong><dd> <code>any</code>
+ </dl>
+<p>
+<a name="conf/verbose"></a><a name="l21"></a>
+<strong>4.2.4: verbosity - Controlling debug output</strong> <a name="confverbosity - Controlling debug output"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Verbosity statements come in two forms: <code>verbosity on</code> or
+ <code>verbosity off</code>. When 'on', log messages to <code>/var/log/messages</code>
+ are generated that show what's going on. (Actually, the
+ messages go to <code>syslog(3)</code>, using facility <code>LOG_DAEMON</code> and
+ priority <code>LOG_INFO</code>. In most (Linux) cases this will mean: output to
+ <code>/var/log/messages</code>. On Mac OSX the messages go to
+ <code>/var/log/system.log</code>.) The keyword <code>verbose</code> is an alias for
+ <code>verbosity</code>.
+ <p><dt><strong>Syntax:</strong><dd> <code>verbosity</code> <em>setting</em> or <code>verbose</code> <em>setting</em>, where
+ <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn
+ verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off.
+ <p><dt><strong>Default:</strong><dd> <code>off</code>
+ </dl>
+<p>
+<a name="conf/dispatchmode"></a><a name="l22"></a>
+<strong>4.2.5: dispatchmode - How are back ends selected</strong> <a name="confdispatchmode - How are back ends selected"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The dispatch mode controls how crossroads selects a back end from
+ a list of active back ends. The below text shows the bare
+ syntax. See section <a href="crossroads.html#howselected">5.1</a> for a textual explanation.
+<p>
+The settings can be:
<p>
<ul>
<li> <code>dispatchmode roundrobin</code>: Simply the 'next in line' is
@@ -657,28 +724,47 @@ The modifier <code>over</code> <em>connections</em> is optional. (The square
<p>
<li> <code>dispatchmode byorder</code>: The first back end is selected
every time, unless it's unavailable. In that case the second
- is taken, and so on.</ul>
+ is taken, and so on.
+<p>
+<li> <code>dispatchmode externalhandler</code> <em>program arguments</em>:
+ This is a special mode, where an external program is delegated
+ the responsibility to say which back end should be used
+ next. In this case, Crossroads will call the external program,
+ and this will of course be slower than one of the 'built-in'
+ dispatch modes. However, this is the ultimate escape when
+ custom-made dispatch modes are needed.
+<p>
+The dispatch mode that uses an <code>externalhandler</code> is
+ discussed separately in section <a href="crossroads.html#externalhandler">5.2</a>.</ul>
<p>
The selection algorithm is only used when clients are serviced that
-aren't part of a sticky HTTP session. This is the case during:
+ aren't part of a sticky HTTP session. This is the case during:
<p>
<ul>
<li> all client requests of a service type <code>any</code>;
<li> new sessions of a service type <code>http</code>.</ul>
<p>
When type <code>http</code> is in effect and a session is underway, then the
-previously used back end is always selected -- regardless of
-dispatching mode.
+ previously used back end is always selected -- regardless of
+ dispatching mode.
<p>
Your 'right' dispatch mode will depend on the type of service. Given
-the fact that crossroads doesn't know (and doesn't care) how to
-estimate load from a network traffic stream, you have to choose an
-appropriate dispatch mode to optimize load balancing. In most cases,
-<code>roundrobin</code> or <code>byconnections</code> will do the job just fine.
-<p>
-<p><dt><strong>A reviving interval definition</strong><dd> is needed when crossroads
-determines that a back end is temporarily unavailable. This will
-happen when:
+ the fact that crossroads doesn't know (and doesn't care) how to
+ estimate load from a network traffic stream, you have to choose an
+ appropriate dispatch mode to optimize load balancing. In most cases,
+ <code>roundrobin</code> or <code>byconnections</code> will do the job just fine.
+ <p><dt><strong>Syntax:</strong><dd> <code>dispatchmode</code> <em>mode</em> (see above for the modes), optionally
+ followed by <code>over</code> <em>number</em>, or when the <em>mode</em> is
+ <code>externalhandler</code>, followed by <em>program</em>.
+ <p><dt><strong>Default:</strong><dd> <code>roundrobin</code>
+ </dl>
+<p>
+<a name="conf/revivinginterval"></a><a name="l23"></a>
+<strong>4.2.6: revivinginterval - Back end wakeup calls</strong> <a name="confrevivinginterval - Back end wakeup calls"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> A reviving interval definition is needed when crossroads
+ determines that a back end is temporarily unavailable. This will
+ happen when:
<p>
<ul>
<li> The back end cannot be reached (network connection
@@ -686,78 +772,156 @@ happen when:
<li> The network connection to the back end suddenly dies.</ul>
<p>
An example of the definition is <code>revivinginterval 10</code>. When this
-reviving interval is given, crossroads will check each 10 seconds
-whether unavailable back ends have woken up yet. A back end is
-considered awake when a network connection to that back end can
-succesfully be established.
-<p>
-<ul>
- <li> Syntax: <code>revivinginterval</code> <em>number</em> <code>;</code>
- <li> Default: 0, meaning no revivals will occur.</ul>
-<p>
-<p><dt><strong>The maximum number of connections</strong><dd> is specified using
-<code>maxconnections</code>. There is one argument; the number of concurrent
-established connections that may be active within one service.
+ reviving interval is given, crossroads will check each 10 seconds
+ whether unavailable back ends have woken up yet. A back end is
+ considered awake when a network connection to that back end can
+ succesfully be established.
+ <p><dt><strong>Syntax:</strong><dd> <code>revivinginterval</code> <em>number</em>, where the number is the interval
+ in seconds.
+ <p><dt><strong>Default:</strong><dd> 0 (no wakeup calls)
+ </dl>
+<p>
+<a name="conf/maxconnections"></a><a name="l24"></a>
+<strong>4.2.7: maxconnections - Limiting concurrent clients at service level</strong> <a name="confmaxconnections - Limiting concurrent clients at service level"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The maximum number of connections is specified using
+ <code>maxconnections</code>. There is one argument; the number of concurrent
+ established connections that may be active within one service.
<p>
'Throttling' the number of connections is a way of preventing Denial of
-Service (DOS) attacks. Without a limit, numerous network connections
-may spawn so many server instances, that the service ultimately breaks
-down and becomes unavailable.
-<p>
-<ul>
- <li> Syntax: <code>maxconnections</code> <em>number</em> <code>;</code>
- <li> Default: 0, meaning that all client connections will be
- accepted.</ul>
-<p>
-<p><dt><strong>The TCP back log size</strong><dd> is a number that controls how many
-'waiting' network connections may be queued, before a client simply
-cannot connect. The syntax is e.g. <code>backlog 5</code> to cause crossroads
-to have 5 waiting connections for 1 active connection.
-The backlog queue shouldn't be too
-high, or clients will experience timeouts before they can actually
-connect. The queue shouldn't be too small either, because clients
-would be simply rejected. Your mileage may vary.
-<p>
-<ul>
- <li> Syntax: <code>backlog</code> <em>number</em> <code>;</code>
- <li> Default: zero, which takes the operating system's default
- value for socket back log size.</ul>
-<p>
-<p><dt><strong>Reporting based: the shared memory key.</strong><dd> Different crossroad
-invocations must 'know' of each others activity. E.g, <code>crossroad
-status</code> must be able to get to the actual state information of all
-running services. This is internally implemented through shared
-memory, which is reserved using a key.
+ Service (DOS) attacks. Without a limit, numerous network connections
+ may spawn so many server instances, that the service ultimately breaks
+ down and becomes unavailable.
+ <p><dt><strong>Syntax:</strong><dd> <code>maxconnections</code> <em>number</em>, where the number specifies the
+ maximum of concurrent connections to the service.
+ <p><dt><strong>Default:</strong><dd> 0, meaning that all connections will be accepted.
+ </dl>
+<p>
+<a name="conf/backlog"></a><a name="l25"></a>
+<strong>4.2.8: backlog - The TCP Back Log size</strong> <a name="confbacklog - The TCP Back Log size"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The TCP back log size is a number that controls how many
+ 'waiting' network connections may be queued, before a client simply
+ cannot connect. The syntax is e.g. <code>backlog 5</code> to cause crossroads
+ to have 5 waiting connections for 1 active connection.
+ The backlog queue shouldn't be too
+ high, or clients will experience timeouts before they can actually
+ connect. The queue shouldn't be too small either, because clients
+ would be simply rejected. Your mileage may vary.
+ <p><dt><strong>Syntax:</strong><dd> <code>backlog</code> <em>number</em>
+ <p><dt><strong>Default:</strong><dd> 0, which takes the operating system's default
+ value for socket back log size.
+ </dl>
+<p>
+<a name="conf/shmkey"></a><a name="l26"></a>
+<strong>4.2.9: shmkey - Shared Memory Access</strong> <a name="confshmkey - Shared Memory Access"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Different Crossroads
+ invocations must 'know' of each others activity. E.g, <code>crossroad
+ status</code> must be able to get to the actual state information of all
+ running services. This is internally implemented through shared
+ memory, which is reserved using a key.
<p>
Normally crossroads will supply a shared memory key, based on the
-service port and bitwise or-ed with a magic number. In situations
-where this conflicts with existing keys (of other programs, having
-their own keys), you may supply a chosen value.
-<p>
-The syntax is e.g. <code>shmkey 123456</code>. The actual key value doesn't
-matter much, as long as it's unique and as long as each invocation of
-crossroads uses it.
-<p>
-<ul>
- <li> Syntax: <code>shmkey</code> <em>number</em> <code>;</code>
- <li> Default: 0, which means that crossroads will 'guess' its
- own key, based on TCP port and a magic number.</ul>
-<p>
-<p><dt><strong>Connection timeouts:</strong><dd> Sometimes, clients simply won't close a network
-connection which leads to unnecessary resource usage. To avoid this,
-one might state e.g. <code>connectiontimeout 300</code>. This instructs crossroads to
-consider a connection where nothing has happened for 300 seconds as
-'finished'. Crossroads will terminate the connection when this timeout
-is exceeded.
+ service port and bitwise or-ed with a magic number. In situations
+ where this conflicts with existing keys (of other programs, having
+ their own keys), you may supply a chosen value.
+<p>
+The actual key value doesn't matter much, as long as it's unique
+ and as long as each invocation of crossroads uses it.
+ <p><dt><strong>Syntax:</strong><dd> <code>shmkey</code> <em>number</em>
+ <p><dt><strong>Default:</strong><dd> 0, which means that crossroads will 'guess' its
+ own key, based on TCP port and a magic number.
+ </dl>
+<p>
+<a name="conf/allow"></a><a name="l27"></a>
+<strong>4.2.10: allow* and deny* - Allowing or denying connections</strong> <a name="confallow* and deny* - Allowing or denying connections"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Crossroads can allow or deny
+ connections based on the IP address of a client. There are four
+ directives that are relevant: <code>allowfrom</code>, <code>allowfile</code>,
+ <code>denyfrom</code> and <code>denyfile</code>. When using <code>allowfrom</code> and
+ <code>denyfrom</code> then the IP addresses to allow or deny connections are
+ stated in <code>/etc/crossroads.conf</code>.
+<p>
+When <code>allow*</code> directives are used, then all connections are denied
+ unless they match the stated allowed IP's. When <code>deny*</code> directives
+ are used, then all connections are allowed unless they match the
+ stated disallowed IP's. When denying and allowing is both used,
+ then the Crossroads checks the deny list first.
+<p>
+The statements <code>allowfrom</code> and <code>denyfrom</code> are followed by a
+ list of filter specifications. The statements <code>allowfile</code> and
+ <code>denyfile</code> are followed by a filename; Crossroads will read
+ filter specifications from those external files. In both cases,
+ Crossroads obtains filter specifications and places them in its
+ lists of allowed or denied IP addresses. The difference between
+ specifying filters in <code>/etc/crossroads.conf</code> or in external
+ files, is that Crossroads will reload the external files when it
+ receives signal 1 (<code>SIGHUP</code>), as in <code>killall -1 crossroads</code>.
+<p>
+The filter specifications must obey the following syntax: it
+ consists of up to
+ four numbers ranging from 0 to 255 and separated by a decimal
+ sign. Optionally a slash follows, with a bitmask which is also a
+ decimal number.
+<p>
+This is probably best explained by a few examples:
<p>
<ul>
- <li> Syntax: <code>connectiontimeout</code> <em>number</em> <code>;</code>
- <li> Default: 0, meaning that crossroads will not try to
- determine timeouts.</ul>
+ <li> <code>allowfrom 10/8;</code> will allow connections from
+ <code>10.*.*.*</code> (a full Class A network). The mask <code>/8</code> means
+ that the first 8 bits of the number (ie., only the <code>10</code>) are
+ significant. On the last 3 positions of the IP address, all
+ numbers are allowed. Given this directive, client connections
+ from e.g. 10.1.1.1 and 10.2.3.4 will be allowed.
+<p>
+<li> <code>allowfrom 10.3/16;</code> will allow all IP addresses that
+ start with <code>10.3</code>.
+<p>
+<li> <code>allowfrom 10.3.1/16;</code> is the same as above. The third
+ byte of the IP address is superfluous because the netmask
+ specifies that only the first 16 bits (2 numbers) are taken
+ into account.
+<p>
+<li> <code>allowfrom 10.3.1.15;</code> allows traffic from only the
+ specified IP address. There is no bitmask; all four numbers
+ are relevant.
+<p>
+<li> <code>allowfrom 10.3.1.15 10.2/16;</code> allows traffic from one
+ IP address <code>10.3.1.15</code> or from a complete Class B network
+ <code>10.2.*.*</code>
+<p>
+<li> <code>allowfile /tmp/myfile.txt;</code> in combination with a file
+ <code>/tmp/myfile.txt</code>, with the contents <code>10.3.1.15 10.2/16</code>,
+ is the same as above.</ul>
+ <p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>allowfrom</code> <em>filter-specificication(s)</em>
+ <li> <code>denyfrom</code> <em>filter-specificication(s)</em>
+ <li> <code>allowfile</code> <em>filename</em>
+ <li> <code>denyfile</code> <em>filename</em></ul>
+ <p><dt><strong>Default:</strong><dd> In absence of these statements, all client IP's are accepted.
+ </dl>
+<p>
+<a name="conf/useraccount"></a><a name="l28"></a>
+<strong>4.2.11: useraccount - Limiting the effective ID of external processes</strong> <a name="confuseraccount - Limiting the effective ID of external processes"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Using the directive <code>useraccount</code>, the effective user and group
+ ID can be restricted. This comes into effect when Crossroads runs
+ external commands, such as:
+ <ul>
+ <li> Hooks for <code>onstart</code>, <code>onend</code> or <code>onfail</code>;
+ <li> External dispatchers, when <code>dispatchmode
+ externalhandler</code> is in effect.</ul>
+ Once a user name for external commands is specified, Crossroads
+ assumes the associated user ID and group ID before running those
+ commands.
+ <p><dt><strong>Syntax:</strong><dd> <code>useraccount</code> <em>username</em>
+ <p><dt><strong>Default:</strong><dd> None; when unspecified, external commands are run with the
+ ID that was in effect when Crossroads was started.
+ </dl>
<p>
-</dl>
-<p>
-<a name="l16"></a>
+<a name="l29"></a>
<h3>4.3: Backend definitions</h3>
<p>
Inside the service definitions as are described in the previous
@@ -788,144 +952,159 @@ balancing and fail over) as long as the backend names differ.
The statements in the backend definition blocks are described in the
following sections.
<p>
-<a name="l17"></a>
-<strong>4.3.1: General Backend Directives</strong>
-<p>
-The following directives are used in all types of services (<code>any</code> or
-<code>http</code>). HTTP-specific directives are shown in section <a href="crossroads.html#httpdirectives">4.3.2</a>.
-<p>
-<dl>
-<p>
-<p><dt><strong>Server:</strong><dd> Each back end must be identified by the network name
- (server name) where it is located. For example: <code>server
- 10.1.1.23</code>, or <code>server web.mydomain.org</code>. A TCP port specifier
- can follow the server name, as in <code>server web.mydomain.org:80</code>.
-<p>
-<ul>
- <li> Syntax: <code>server</code> <em>servername</em> <code>;</code>
- <li> Or: <code>server</code> <em>servername</em><code>:</code><em>port</em> <code>;</code>
- <li> There is no default. This is a required setting.</ul>
-<p>
-<p><dt><strong>Port:</strong><dd> When the <code>server</code> specifier doesn't include a TCP
- port, then this statement is used to define the port at which the
- back end expects its traffic. There is one argument, the (numeric)
- port number.
-<p>
-<ul>
- <li> Syntax: <code>port</code> <em>number</em> <code>;</code>
- <li> There is no default. The port must be defined either in
- the <code>server</code> setting or using the <code>port</code> specifier.</ul>
-<p>
-<p><dt><strong>Verbosity:</strong><dd> Similar to <code>service</code> specifications, a
- <code>backend</code> can have its own verbosity (<code>on</code> or <code>off</code>). When
- <code>on</code>, traffic to and fro this back end is reported.
-<p>
-<ul>
- <li> Syntax: <code>verbosity</code> <em>setting</em> <code>;</code>
- <li> Or: <code>verbose</code> <em>setting</em> <code>;</code>
- <li> where <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn
- verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off.
- <li> Default: <code>off</code>.</ul>
-<p>
-<p><dt><strong>Maxconnections:</strong><dd> This setting states how many concurrent connections
- a back end connection may accept. Note that there is also a
- <code>maxconnections</code> statement for the overall service description.
-<p>
-The difference is that a <code>maxconnections</code> statement at the level of
- a service description avoids too many hits from the outside (DOS
- prevention). A <code>maxconnections</code> statement at the level of a back end
- description makes sure that this particular back end doesn't get
- overloaded.
-<p>
-<ul>
- <li> Syntax: <code>maxconnections</code> <em>number</em> <code>;</code>
- <li> where <em>number</em> is the maximum number of concurrent
- client connections.
- <li> Default: 0, meaning that there is no limit.</ul>
-<p>
-<p><dt><strong>Weight:</strong><dd> To influence how backends are selected by size or by
- duration, a backend can specify its 'weight' in the process. The
- higher the weight, the less likely a back end will be chosen. The
- default is 1.
+Some directives (<code>stickycookie</code> etc.) only have effect when
+Crossroads treats the network traffic as a stream of HTTP messages;
+i.e., when the service is declared with <code>type http</code>. Incase of
+<code>type any</code>, the HTTP-specific directives have no effect.
+<p>
+<a name="conf/server.yo"></a><a name="l30"></a>
+<strong>4.3.1: server - Specifying the back end address</strong> <a name="confserver - Specifying the back end address"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Each back end must be identified by the network name
+ (server name) where it is located. For example: <code>server
+ 10.1.1.23</code>, or <code>server web.mydomain.org</code>. A TCP port specifier
+ can follow the server name, as in <code>server web.mydomain.org:80</code>.
+ <p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>server</code> <em>servername</em>, where <em>servername</em> is a
+ network name or IP address;
+ <li> <code>server</code> <em>servername:port</em></ul>
+ <p><dt><strong>Default:</strong><dd> There is no default. This is a required setting.
+ </dl>
+<p>
+<a name="conf/verbose-backend.yo"></a><a name="l31"></a>
+<strong>4.3.2: verbosity - Controlling verbosity at the back end level</strong> <a name="confverbosity - Controlling verbosity at the back end level"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Similar to <code>service</code> specifications, a
+ <code>backend</code> can have its own verbosity (<code>on</code> or <code>off</code>). When
+ <code>on</code>, traffic to and fro this back end is reported.
+ <p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>verbosity</code> <em>setting</em>, or
+ <li> <code>verbose</code> <em>setting</em>, where <em>setting</em> is <code>true</code>,
+ <code>yes</code> or <code>on</code>, or <code>false</code>, <code>no</code>, <code>off</code> to turn it
+ off.</ul>
+ <p><dt><strong>Default:</strong><dd> <code>off</code>
+ </dl>
+<p>
+<a name="conf/weight"></a><a name="l32"></a>
+<strong>4.3.3: weight - When a back end is more equal than others</strong> <a name="confweight - When a back end is more equal than others"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> To influence how backends are selected, a backend can specify its
+ 'weight' in the process. The higher the weight, the less likely a
+ back end will be chosen. The default is 1.
<p>
The weighing mechanism only applies to the dispatch modes
- <code>random</code>, <code>byconnections</code>, <code>bysize</code> and <code>byduration</code>.
- The weight is in fact a penalty factor. E.g., if backend A has
- <code>weight 2</code> and backend B has <code>weight 1</code>, then backend B will
- be selected all the time, until its usage parameter is twice as
- large as the parameter of A. Think of it as a 'sluggishness' statement.
-<p>
-<ul>
- <li> Syntax: <code>weight</code> <em>number</em> <code>;</code>
- <li> Default: 1</ul>
-<p>
-<p><dt><strong>Decay:</strong><dd> To make sure that a 'spike' of activity doesn't
- influence the perceived load of a back end forever, you may
- specify a certain decay. E.g, the statement <code>decay 10</code> makes
- sure that the load that crossroads computes for this back end (be
- it in seconds or in bytes) is decreased by 10% each time that
- <strong>an other</strong> back end is hit. Decays are not applied to the count
- of concurrent connections.
+ <code>random</code>, <code>byconnections</code>, <code>bysize</code> and <code>byduration</code>.
+ The weight is in fact a penalty factor. E.g., if backend A has
+ <code>weight 2</code> and backend B has <code>weight 1</code>, then backend B will
+ be selected all the time, until its usage parameter is twice as
+ large as the parameter of A. Think of it as a 'sluggishness'
+ statement.
+ <p><dt><strong>Syntax:</strong><dd> <code>weight</code> <em>number</em>; the higher the number, the more 'sluggish'
+ a back end is
+ <p><dt><strong>Default:</strong><dd> 1; all back ends have equal weight.
+ </dl>
+<p>
+<a name="conf/decay"></a><a name="l33"></a>
+<strong>4.3.4: decay - Levelling out activity of a back end</strong> <a name="confdecay - Levelling out activity of a back end"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> To make sure that a 'spike' of activity doesn't
+ influence the perceived load of a back end forever, you may
+ specify a certain decay. E.g, the statement <code>decay 10</code> makes
+ sure that the load that crossroads computes for this back end (be
+ it in seconds or in bytes) is decreased by 10% each time that
+ <strong>an other</strong> back end is hit. Decays are not applied to the count
+ of concurrent connections.
<p>
This means that when a given back end is hit, then its usage data
- of the transferred bytes and the connection duration are updated
- using the actual number of bytes and actual duration. However,
- when a different back end is hit, then the usage data are
- decreased by the specified decay.
+ of the transferred bytes and the connection duration are updated
+ using the actual number of bytes and actual duration. However,
+ when a different back end is hit, then the usage data are
+ decreased by the specified decay.
+ <p><dt><strong>Syntax:</strong><dd> <code>decay</code> <em>number</em>, where <em>number</em> is a percentage that
+ decreases the back end usage data when other back ends are
+ hit.
+ <p><dt><strong>Default:</strong><dd> 0, meaning that no decay is applied to usage statistics.
+ </dl>
+<p>
+<a name="conf/onhooks"></a><a name="l34"></a>
+<strong>4.3.5: onstart, onend, onfail - Action Hooks</strong> <a name="confonstart, onend, onfail - Action Hooks"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The three directives <code>onstart</code>, <code>onend</code> and <code>onfail</code> can be
+ specified to start system commands (external programs) when a
+ connection to a back end starts, fails or ends:
+ <ul>
+ <li> <code>onstart</code> commands will be run when Crossroads
+ successfully connects to a back end, and starts servicing;
+ <li> <code>onend</code> commands will be run when a (previously
+ established) connection stops;
+ <li> <code>onfail</code> commands will be run when Crossroads tries to
+ contact a back end to serve a client, but the back end can't
+ be reached.</ul>
+<p>
+The format is always <code>on</code><em>type</em> <em>command</em>. The <em>command</em>
+ is an external program, optionally followed by arguments. The
+ command is expanded according to the following table:
<p>
<ul>
- <li> Syntax: <code>decay</code> <em>number</em> <code>;</code>
- <li> where <em>number</em> is a percentage that decreases the back
- data when other back ends are hit
- <li> Default: 0, meaning that no decay is applied to usage
- statistics.</ul>
-<p>
-<p><dt><strong>Event triggers:</strong><dd> As special 'hooks' for actions, two triggers
- are available: <code>onfailure</code> and <code>onsuccess</code>. The argument to
- the triggers is a system command that is executed when a connection
- with the back end either fails or succeeds.
-<p>
-<ul>
- <li> Syntax: <code>onfailure</code> <em>commandline</em> <code>;</code> and
- <code>onsuccess</code> <em>commandline</em> <code>;</code>
- <li> There is no default.</ul>
-<p>
-<p><dt><strong>Debugging and Performance aids:</strong><dd> Two directives are available
- to log network traffic to files. They are <code>trafficlog</code> and
- <code>throughputlog</code>.
-<p>
-<ul>
- <li> Syntax: <code>trafficlog</code> <em>filename</em> <code>;</code>
- <li> And: <code>throughputlog</code> <em>filename</em> <code>;</code>
- <li> Default: none</ul>
+ <li> <code>%a</code> is the availability of the current back end, when
+ a current back end is established;
+ <li> <code>%1a</code> is the availability of the first back end (0 when
+ unavailable, 1 if available); <code>%2a</code> is the availability of
+ the second back end, and so on;
+ <li> <code>%b</code> is the name of the current back end, when one is
+ established;
+ <li> <code>%1b</code> is the name of the first back end, <code>%2b</code> of the
+ second back end, and so on;
+ <li> <code>%e</code> is the count of seconds since start of epoch
+ (January 1st 1970 GMT);
+ <li> <code>%r</code> is the IP address of the client that requests a
+ connection and for whom the external dispatcher should compute
+ a back end;
+ <li> <code>%s</code> is the name of the current service that the client
+ connected to;
+ <li> <code>%t</code> is the current local time in ASCII format, in
+ <em>YYYY-MM-DD/hhh:mm:ss</em>;
+ <li> <code>%T</code> is the current GMT time in ASCIII format;
+ <li> <code>%v</code> is the Crossroads version;
+ <li> Any other chararacter following a <code>%</code> sign is taken
+ literally; e.g. <code>%z</code> is just a z.</ul>
+<p>
+<p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>onstart</code> <em>commandline</em>
+ <li> <code>onend</code> <em>commandline</em>
+ <li> <code>onfail</code> <em>commandline</em>
+ <li> <code>onsuccess</code> <em>commandline</em></ul>
+ <p><dt><strong>Default:</strong><dd> There is no default. Normally no external programs are run upon
+ connection, success or failure of a back end.
+ </dl>
+<p>
+<a name="conf/trafficlog"></a><a name="l35"></a>
+<strong>4.3.6: trafficlog and throughputlog - Debugging and Performance Aids</strong> <a name="conftrafficlog and throughputlog - Debugging and Performance Aids"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Two directives are available
+ to log network traffic to files. They are <code>trafficlog</code> and
+ <code>throughputlog</code>.
<p>
The <code>trafficlog</code> statement causes all traffic to be logged in
- hexadecimal format. Each line is prefixed by <code>B</code> or <code>C</code>,
- depending on whether the information was received from the back
- end or from the client.
+ hexadecimal format. Each line is prefixed by <code>B</code> or <code>C</code>,
+ depending on whether the information was received from the back
+ end or from the client.
<p>
The <code>throughputlog</code> statement writes shorthand transmissions to
- its log, accompanied by timings.
-<p>
-</dl>
-<p>
-<a name="l18"></a>
-<strong>4.3.2: HTTP-related Backend Directives</strong> <a name="httpdirectives"></a>
-<p>
-The following directives are specific for HTTP-type services; i.e.,
-services with a specification <code>type http</code>.
-<p>
-It is inevitable that when Crossroads handles services of <code>type
-http</code>, more processing is necessary. Crossroads has to unpack the TCP
-payload in order to do its header magic; which leads to performance
-impact.
-<p>
-<dl>
-<p>
-<p><dt><strong>Session Stickiness:</strong><dd> The directive <code>stickycookie</code> <em>value</em>
- causes Crossroads to unpack clients' requests, to check for
- <em>value</em> in the cookies. When found, the message is routed to the
- back end having the appropriate <code>stickycookie</code> directive.
+ its log, accompanied by timings.
+ <p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>trafficlog</code> <em>filename</em>
+ <li> <code>throughputlog</code> <em>filename</em></ul>
+ <p><dt><strong>Default:</strong><dd> none
+ </dl>
+<p>
+<a name="conf/stickycookie"></a><a name="l36"></a>
+<strong>4.3.7: stickycookie - Back end selection with an HTTP cookie</strong> <a name="confstickycookie - Back end selection with an HTTP cookie"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> The directive <code>stickycookie</code> <em>value</em>
+ causes Crossroads to unpack clients' requests, to check for
+ <em>value</em> in the cookies. When found, the message is routed to the
+ back end having the appropriate <code>stickycookie</code> directive.
<p>
E.g., consider the following configuration:
<p>
@@ -945,24 +1124,30 @@ service ... {
<p>
When clients' messages contain cookies named <code>BalancerID</code> with
- the value <code>first</code>, then such messages are routed to backend
- <code>one</code>. When the value is <code>second</code> then they are routed to the
- backend <code>two</code>.
+ the value <code>first</code>, then such messages are routed to backend
+ <code>one</code>. When the value is <code>second</code> then they are routed to the
+ backend <code>two</code>.
<p>
There are basically to provide such cookies to a browser. First, a
- back end can insert such a cookie into the HTTP response. E.g.,
- the webserver of back end <code>one</code> might insert a cookie named
- <code>BalancerID</code>, having value <code>first</code>.
- Second, Crossroads can insert such cookies using a carefully
- crafted directive <code>addclientheader</code>. See below.
-<p>
-<p><dt><strong>Header modification:</strong><dd> Crossroads understands the following
- header modification directives: <code>addclientheader</code>,
- <code>appendclientheader</code>, <code>setclientheader</code>, <code>addserverheader</code>,
- <code>appendserverheader</code>, <code>setserverheader</code>.
+ back end can insert such a cookie into the HTTP response. E.g.,
+ the webserver of back end <code>one</code> might insert a cookie named
+ <code>BalancerID</code>, having value <code>first</code>.
+ Second, Crossroads can insert such cookies using a carefully
+ crafted directive <code>addclientheader</code>.
+ <p><dt><strong>Syntax:</strong><dd> <code>stickycookie</code> <em>cookievalue</em>
+ <p><dt><strong>Default:</strong><dd> There is no default.
+ </dl>
+<p>
+<a name="conf/addclientheader"></a><a name="l37"></a>
+<strong>4.3.8: HTTP Header Modification Directives</strong> <a name="confHTTP Header Modification Directives"></a>
+ <dl>
+ <p><dt><strong>Description:</strong><dd> Crossroads understands the following
+ header modification directives: <code>addclientheader</code>,
+ <code>appendclientheader</code>, <code>setclientheader</code>, <code>addserverheader</code>,
+ <code>appendserverheader</code>, <code>setserverheader</code>.
<p>
The directive names always consist of
- <em>Action</em><em>Destination</em><code>header</code>, where:
+ <em>Action</em><em>Destination</em><code>header</code>, where:
<p>
<ul>
<li> The action is <code>add</code>, <code>append</code> or <code>insert</code>.
@@ -992,23 +1177,37 @@ The directive names always consist of
<code>client</code>, then Crossroads will apply such directives to
backend responses that are shuttled to the browser.</ul>
<p>
-The syntax of the directives is e.g. <code>addclientheader
- "X-Processed-By: Crossroads";</code>. The directives expect one
- argument; a string, consisting of a header name, a colon, and a
- header value. The directive ends with a semicolon.
+The format of the directives is e.g. <code>addclientheader
+ "X-Processed-By: Crossroads"</code>. The directives expect one
+ argument; a string, consisting of a header name, a colon, and a
+ header value. As usual, the directive must end with a semicolon.
<p>
The header value may contain one of the following formatting
- directives:
+ directives:
<p>
<ul>
- <li> <code>%r</code> is expanded to the real IP address of a client;
- <li> <code>%t</code> is expanded to a timestamp of the local time;
- <li> <code>%T</code> is expanded to a timestamp of Greenwich Mean Time;
- <li> <code>%v</code> is expanded to the Crossroads version;
- <li> <code>%</code><em>x</em> (where <em>x</em> is any other character) is
- expanded to <em>x</em>. E.g., <code>%%</code> is a literal % sign.</ul>
-<p>
-<p><dt><strong>Common Uses</strong><dd>
+ <li> <code>%a</code> is the availability of the current back end, when
+ a current back end is established;
+ <li> <code>%1a</code> is the availability of the first back end (0 when
+ unavailable, 1 if available); <code>%2a</code> is the availability of
+ the second back end, and so on;
+ <li> <code>%b</code> is the name of the current back end, when one is
+ established;
+ <li> <code>%1b</code> is the name of the first back end, <code>%2b</code> of the
+ second back end, and so on;
+ <li> <code>%e</code> is the count of seconds since start of epoch
+ (January 1st 1970 GMT);
+ <li> <code>%r</code> is the IP address of the client that requests a
+ connection and for whom the external dispatcher should compute
+ a back end;
+ <li> <code>%s</code> is the name of the current service that the client
+ connected to;
+ <li> <code>%t</code> is the current local time in ASCII format, in
+ <em>YYYY-MM-DD/hhh:mm:ss</em>;
+ <li> <code>%T</code> is the current GMT time in ASCIII format;
+ <li> <code>%v</code> is the Crossroads version;
+ <li> Any other chararacter following a <code>%</code> sign is taken
+ literally; e.g. <code>%z</code> is just a z.</ul>
<p>
The following examples show common uses of header modifications.
<p>
@@ -1090,19 +1289,38 @@ service ... {
}
}
</pre>
-
-<p>
</dl>
+ <p><dt><strong>Syntax:</strong><dd> <ul>
+ <li> <code>addclientheader</code> <em>Headername: headervalue</em> to add a
+ header in the traffic towards the client, even when another
+ header <em>Headername</em> exists;
+ <li> <code>appendclientheader</code> <em>Headername: headervalue</em> to
+ append <em>headervalue</em> to an existing header <em>Headername</em>
+ in the traffic towards the client,
+ or to add the whole header alltogether;
+ <li> <code>setclientheader</code> <em>Headername: headervalue</em> to
+ overwrite an existing header in the traffic towards the
+ client, or to add such a header;
+ <li> <code>addserverheader</code> <em>Headername: headervalue</em> to add a
+ header in the traffic towards the server, even when another
+ header <em>Headername</em> exists;
+ <li> <code>appendserverheader</code> <em>Headername: headervalue</em> to
+ append <em>headervalue</em> to an existing header <em>Headername</em>
+ in the traffic towards the server,
+ or to add the whole header alltogether;
+ <li> <code>setserverheader</code> <em>Headername: headervalue</em> to
+ overwrite an existing header in the traffic towards the
+ server, or to add such a header.</ul>
+ <p><dt><strong>Default:</strong><dd> There is no default.
+ </dl>
<p>
-</dl>
-<p>
-<a name="l19"></a>
+<a name="l38"></a>
<h2>5: Tips, Tricks and Remarks</h2>
<a name="tips"></a>The following sections elaborate on the directives as described in
section <a href="crossroads.html#config">4</a> to illustrate how crossroads works and to help you
achieve the "optimal" balancing configuration.
<p>
-<a name="l20"></a>
+<a name="l39"></a>
<h3>5.1: How back ends are selected in load balancing</h3><a name="howselected"></a>
<p>
In order to tune your load balancing, you'll need to understand how
@@ -1111,7 +1329,7 @@ section we'll focus on the dispatching modes <code>bysize</code>, <code>bydurati
and <code>byconnections</code> only. The other dispatching types are
self-explanatory.
<p>
-<a name="l21"></a>
+<a name="l40"></a>
<strong>5.1.1: Bysize, byduration or byconnections?</strong>
<p>
As stated before, crossroads doesn't know 'what a service does' and
@@ -1161,7 +1379,7 @@ E.g., consider a database connection. What's
<p>
</ul>
<p>
-<a name="l22"></a>
+<a name="l41"></a>
<strong>5.1.2: Averaging size and duration</strong>
<p>
The configuration statement <code>dispatchmode bysize</code> or <code>byduration</code>
@@ -1182,7 +1400,7 @@ In contrast, when e.g. <code>over 3</code> is in effect, then a sudden load
does show up -- because it highly contributes to the average of three
connections.
<p>
-<a name="l23"></a>
+<a name="l42"></a>
<strong>5.1.3: Specifying decays</strong>
<p>
Decays are also only relevant when crossroads computes the 'next best
@@ -1236,7 +1454,7 @@ service soap {
</pre>
<p>
-<a name="l24"></a>
+<a name="l43"></a>
<strong>5.1.4: Adjusting the weights</strong>
<p>
The back end modifier <code>weight</code> is useful in situations where your
@@ -1290,7 +1508,7 @@ both A and B crash). Note also that A's usage data decay much faster
than B's and C's: we're assuming that this big server recovers quicker
than its smaller siblings.
<p>
-<a name="l25"></a>
+<a name="l44"></a>
<strong>5.1.5: Throttling the number of concurrent connections</strong>
<p>
If you suspect that your service may occasionally receive 'spikes' of
@@ -1320,8 +1538,488 @@ too much, a situation may occur where that back end is about to be
hit. A <code>maxconnections</code> statement on the level of that back may then
protect it.
<p>
-<a name="l26"></a>
-<h3>5.2: HTTP Session Stickiness</h3>
+<a name="l45"></a>
+<h3>5.2: Using an external program to dispatch</h3>
+<a name="externalhandler"></a>
+<p>
+As mentioned before, Crossroads supports several built-in dispatch
+modes. However, you are always free to hook-in your own dispatch mode
+that determines the next back end using your own specific
+algorithm. This section explains how to do it.
+<p>
+<a name="l46"></a>
+<strong>5.2.1: Configuring the external handler</strong>
+<p>
+First, the <code>dispatchmode</code> statement needs to inform Crossroads that
+an external program will do the job. The syntax is: <code>dispatchmode
+externalhandler</code> <em>program arguments</em>. The <em>program</em> must point to
+an executable program that will be started by Crossroads. The
+specifier <em>arguments</em> can be anything you want; those will be the
+arguments to Crossroads. You can however use the following special
+format specifiers:
+<p>
+<ul>
+ <li> <code>%a</code> is the availability of the current back end, when
+ a current back end is established;
+ <li> <code>%1a</code> is the availability of the first back end (0 when
+ unavailable, 1 if available); <code>%2a</code> is the availability of
+ the second back end, and so on;
+ <li> <code>%b</code> is the name of the current back end, when one is
+ established;
+ <li> <code>%1b</code> is the name of the first back end, <code>%2b</code> of the
+ second back end, and so on;
+ <li> <code>%e</code> is the count of seconds since start of epoch
+ (January 1st 1970 GMT);
+ <li> <code>%r</code> is the IP address of the client that requests a
+ connection and for whom the external dispatcher should compute
+ a back end;
+ <li> <code>%s</code> is the name of the current service that the client
+ connected to;
+ <li> <code>%t</code> is the current local time in ASCII format, in
+ <em>YYYY-MM-DD/hhh:mm:ss</em>;
+ <li> <code>%T</code> is the current GMT time in ASCIII format;
+ <li> <code>%v</code> is the Crossroads version;
+ <li> Any other chararacter following a <code>%</code> sign is taken
+ literally; e.g. <code>%z</code> is just a z.</ul>
+<p>
+Note that the format specifiers such as <code>%b</code> don't make sense in the
+phase in which an external handler is called, since there is no
+current back end yet (the job of the handler is to supply one).
+<p>
+<a name="l47"></a>
+<strong>5.2.2: Writing the external handler</strong>
+<p>
+The external handler is activated using the arguments that are
+specified in <code>/etc/crossroads.conf</code>. The external handler can do
+whatever it wants, but ultimately, it must write a back end name on
+its <em>stdout</em>. Crossroads reads this, and if the back end is
+available, uses that back end for the connection.
+<p>
+<a name="l48"></a>
+<strong>5.2.3: Examples of external handlers</strong>
+<p>
+This section shows some examples of Crossroads configurations
+vs. external handlers. The sample handlers that are shown here, are
+also included in the Crossroads distribution, under the directory
+<code>etc/</code>. Also note that the examples shown here are just
+quick-and-dirty Perl scripts, meant to illustrate only. Your
+applications may need other external handlers, but you can use the
+shown scripts as a starting point.
+<p>
+<p><strong>Round-robin dispatching</strong><br>
+<p>
+This example is trivial in the sense that round-robin dispatching is
+already built into Crossroads, so
+that using an external handler for this purpose only slows down
+Crossroads. However, it's a good starting example.
+<p>
+The Crossroads configuration is shown below:
+<p>
+<pre>
+service test {
+ port 8001;
+ verbosity on;
+ revivinginterval 5;
+
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/dispatcher-roundrobin
+ %1b %1a %2b %2a;
+
+ backend testone {
+ server localhost:3128;
+ verbosity on;
+ }
+ backend testtwo {
+ server locallhost:3128;
+ verbosity on;
+ }
+}
+</pre>
+
+<p>
+The relevant <code>dispatchmode</code> statement invokes the external program
+<code>dispatcher-roundrobin</code> with four arguments: the name of the first
+back end (<code>testone</code>), its availability (0 or 1), the name of the
+second back end (<code>testtwo</code>) and its availability (0 or 1).
+<p>
+The external handler, which is also included in the Crossroads
+distribution, is shown below. It is a Perl script.
+<p>
+<pre>
+#!/usr/bin/perl
+
+use strict;
+
+# Example of a round-robin external dispatcher. This is totally
+# superfluous, Crossroads has this on-board; if you use the external
+# program for determining round-robin dispatching, then you'll only
+# slow things down. This script is just meant as an example.
+
+# Globals / configuration
+# -----------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $statefile = '/tmp/rr.last'; # Where we keep the last used
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+}
+
+# Read the last used back end
+# ---------------------------
+sub readlast() {
+ my $ret;
+
+ if (open (my $if, $statefile)) {
+ $ret = <$if>;
+ chomp ($ret);
+ close ($if);
+ msg ("Last used back end: $ret\n");
+ return ($ret);
+ }
+ msg ("No last-used back end (yet)\n");
+ return (undef);
+}
+
+# Write back the last used back end, reply to Crossroads and stop
+# ---------------------------------------------------------------
+sub reply ($) {
+ my $last = shift;
+
+ if (open (my $of, ">$statefile")) {
+ print $of ("$last\n");
+ }
+ print ("$last\n");
+ exit (0);
+}
+
+# Main starts here
+# ----------------
+
+# Collect the cmdline arguments. We expect pairs of backend-name /
+# backend-availablility, and we'll store only the available ones.
+msg ("Dispatch request received\n");
+my @backend;
+for (my $i = 0; $i <= $#ARGV; $i += 2) {
+ push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]);
+}
+msg ("Available back ends: @backend\n");
+
+# Let's see what the last one is. If none found, then we return the
+# first available back end. Otherwise we need to go thru the list of
+# back ends, and return the next one in line.
+my $last = readlast();
+if ($last eq '') {
+ msg ("Returning first available back end $backend[0]\n");
+ reply ($backend[0]);
+}
+
+# There **was** a last back end. Try to match it in the list,
+# then return the next-in-line.
+for (my $i = 0; $i < $#backend; $i++) {
+ if ($last eq $backend[$i]) {
+ msg ("Returning next back end ", $backend[$i + 1], "\n");
+ reply ($backend[$i + 1]);
+ }
+}
+
+# No luck.. run back to the first one.
+msg ("Returning first back end $backend[0]\n");
+reply ($backend[0]);
+</pre>
+
+<p>
+The working of the script is basically as follows:
+<p>
+<ul>
+ <li> The argument list is scanned. Back ends that are
+ available are collected in an array <code>@backend</code>.
+<p>
+<li> The script queries a state file <code>/tmp/rr.last</code>. If a
+ back end name occurs there, then the next back end is looked
+ up in <code>@backend</code> and returned to Crossroads. If no last back
+ is unknown or can't be matched, then the first available back
+ end (first element of <code>@backend</code>) is returned to Crossroads.
+<p>
+<li> Informing Crossroads is done via the subroutine
+ <code>reply()</code>. This code writes the selected back end to file
+ <code>/tmp/rr.last</code> (for future usage) and prints the back end
+ name to <em>stdout</em>.
+<p>
+<li> The script logs its actions to a file
+ <code>/tmp/exthandler.log</code>. This log file can be inspected for
+ the script's actions.</ul>
+<p>
+<p><strong>Dispatching by the client IP address</strong><br>
+<p>
+The following example shows a useful real-life situation. The
+situation is as follows:
+<p>
+<ul>
+ <li> Crossroads is used as a single-address point to forward
+ Remote Desktop requests to a farm of Windows systems, where
+ users can work via remote access;
+<p>
+<li> However, users may stop their session, and when they
+ re-connect, they expect to be sent to the Windows system that
+ they had worked on previously;
+<p>
+<li> Client PC's have their distinct IP addresses, which
+ distinguishes them.
+<p>
+<li> Of four windows systems, two are large servers, and two
+ are small ones. We'll want to assign large servers to clients
+ when we have a choice.</ul>
+<p>
+The requirements resemble session stickiness in HTTP, except that the remote
+desktop protocol doesn't support stickiness. This situation is a
+perfect example of how an external handler can help:
+<p>
+<ul>
+ <li> A suitable dispatch mode isn't yet available in
+ Crossroads, but can be easily coded in an external handler;
+<p>
+<li> The potential delay due to the calling of an external
+ handler won't even be noticed. This is a network service where
+ the connection time isn't critical; we'd expect only a few
+ (albeit lengthy) TCP connections.</ul>
+<p>
+The approach to the solution of this problem uses several external
+program hooks:
+<p>
+<ul>
+ <li> An external dispatcher handler will be responsible for
+ suggesting a back end, given a client IP and given the current
+ timestamp. This handler will consult an internal
+ administration to see whether the stated IP address should
+ re-use a back end, or to determine which back end is free for usage.
+ <li> An external hook <code>onstart</code> will be responsible for
+ updating the internal administration; i.e., to flag a back end
+ as 'occupied'.
+ <li> The external hooks <code>onfailure</code> and <code>onend</code> will be
+ responsible for flagging a back end as 'free' again; i.e., for
+ erasing any previous information that states that the back end
+ was occupied.</ul>
+<p>
+The Crossroads configuration is shown below. Only four Windows back
+ends are shown. Each back end is configured on a
+given IP address, port 3389, and is limited to one concurrent connection
+(otherwise a new user might 'steal' a running desktop session).
+<p>
+<pre>
+service rdp {
+ port 3389;
+ revivinginterval 5;
+
+ /* rdp-helper dispatch IP STAMP ... will suggest a back end to use,
+ * arguments are for all back ends: name, availability, weight */
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e
+ %1b %1a %1w
+ %2b %2a %2w
+ %3b %3a %3w
+ %4b %4a %4w;
+
+ backend win1 {
+ server 10.1.1.1:3389;
+ maxconnections 1;
+ /* rdp-helper start IP STAMP BACKEND will log the actual start
+ * of a connection;
+ * rdp-helper end IP will log the ending of a connection */
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win2 {
+ server 10.1.1.2:3389;
+ maxconnections 1;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win3 {
+ server 10.1.1.3:3389;
+ maxconnections 1;
+ weight 2;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win4 {
+ server 10.1.1.4:3389;
+ maxconnections 1;
+ weight 3;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+}
+</pre>
+
+<p>
+Depending on the dispatcher stage, the exernal handler <code>rdp-helper</code>
+is invoked in different ways:
+<p>
+<dl>
+ <p><dt><strong>During dispatching</strong><dd> the helper is called to suggest a back
+ end. The arguments are an action indicator <code>dispatch</code>, the
+ client's IP address, the timestamp, and four triplets that
+ represent back ends: per back end its name, its availability,
+ and its weight. The purpose of the helper is to tell
+ Crossroads which back end to use.
+<p>
+<p><dt><strong>During connection start</strong><dd> the helper will be invoked to
+ inform it of the start of a connection, given a client IP
+ address.
+<p>
+<p><dt><strong>When a connection terminates</strong><dd> the helper will be invoked
+ to inform it that the connection has ended.</dl>
+<p>
+Here's the external handler as Perl script. It uses the module
+<code>GDBM_File</code> which most likely will not be part of standard Perl
+distributions, but can be added using CPAN. (Alternatively, any other
+database module can be used.)
+<p>
+<pre>
+#!/usr/bin/perl
+
+use strict;
+use GDBM_File;
+
+# Global variables and configuration
+# ----------------------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $cdb = '/tmp/client.db'; # GDBM database of clients
+my %db; # .. and memory representation of it
+my $timeout = 24*60*60; # Timeout of a connection in secs
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+ close ($of);
+}
+
+# Reply a back end to the caller and stop processing.
+# ---------------------------------------------------
+sub reply ($) {
+ my $b = shift;
+ msg ("Suggesting $b to Crossroads.\n");
+ print ("$b\n");
+ exit (0);
+}
+
+# Is a value in an array
+# ----------------------
+sub inarray {
+ my $val = shift;
+ for my $other (@_) {
+ return (1) if ($other eq $val);
+ }
+ return (0);
+}
+
+# A connection is starting
+# ------------------------
+sub start {
+ my ($ip, $stamp, $backend) = @_;
+ msg ("Logging START of connection for IP $ip on stamp $stamp, ",
+ "back end $backend\n");
+ $db{$ip} = "$backend:$stamp";
+}
+
+# A connection has ended
+# ----------------------
+sub end {
+ my $ip = shift;
+ msg ("Logging END of connection for IP $ip\n");
+ $db{$ip} = undef;
+}
+
+# Request to determine a back end
+# -------------------------------
+sub dispatch {
+ my $ip = shift;
+ my $stamp = shift;
+
+ msg ("Request to dispatch IP $ip on stamp $stamp\n");
+
+ # Read the next arguments. They are triplets of
+ # backend-name / availability / weight. Store if the back end is
+ # available.
+ my (@backends, @weights);
+ for (my $i = 0; $i < $#_; $i += 3) {
+ if ($_[$i + 1] != 0) {
+ push (@backends, $_[$i]);
+ push (@weights, $_[$i + 2]);
+ msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n");
+ }
+ }
+
+ # See if this is a reconnect by a previously seen client IP. We'll
+ # treat this as a reconnect if the timeout wasn't yet exceeded.
+ if ($db{$ip} ne '') {
+ my ($last_backend, $last_stamp) = split (/:/, $db{$ip});
+ msg ("IP $ip had last connected on $last_stamp to $last_backend\n");
+ if ($stamp < $last_stamp + $timeout) {
+ msg ("Timeout not yet exceeded, this may be a reconnect\n");
+ # We'll allow a reconnect only if the stated last_backend is
+ # free (sanity check).
+ if (inarray ($last_backend, @backends)) {
+ msg ("Last back end $last_backend is available, ",
+ "letting through\n");
+ reply ($last_backend);
+ } else {
+ msg ("Last used back end isn't free, suggesting a new one\n");
+ }
+ } else {
+ msg ("Timeout exceeded, suggesting a new back end\n");
+ }
+ } else {
+ msg ("Np preveious connection data, suggesting a new back end\n");
+ }
+
+ my $bestweight = -1;
+ my $bestbackend;
+ for (my $i = 0; $i <= $#weights; $i++) {
+ if ($bestweight == -1 or $bestweight > $weights[$i]) {
+ $bestweight = $weights[$i];
+ $bestbackend = $backends[$i];
+ }
+ }
+
+ msg ("Best back end: $bestbackend (given weight $bestweight)\n");
+ reply ($bestbackend);
+}
+
+# Main starts here
+# ----------------
+msg ("Start of run, attaching GDBM database '$cdb'\n");
+tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600);
+
+# The first argument must be an action 'dispatch', 'start' or 'end'.
+# Depending on the action, we do stuff.
+my $action = shift (@ARGV);
+if ($action eq 'dispatch') {
+ dispatch (@ARGV);
+} elsif ($action eq 'start') {
+ start (@ARGV);
+} elsif ($action eq 'end') {
+ end (@ARGV);
+} else {
+ print STDERR ("Usage: rdp-helper {dispatch|start|end} args\n");
+ exit (1);
+}
+</pre>
+
+<p>
+<a name="l49"></a>
+<h3>5.3: HTTP Session Stickiness</h3>
<p>
This section focuses on HTTP session stickiness. This term refers to
the ability of a balancer to route a conversation between browser and
@@ -1329,8 +2027,8 @@ a backend farm always to the same back end. In other words: once a
back end is selected by the balancer, it will remain the back end of
choice, even for subsequent connections.
<p>
-<a name="l27"></a>
-<strong>5.2.1: Don't use stickiness!</strong>
+<a name="l50"></a>
+<strong>5.3.1: Don't use stickiness!</strong>
<p>
The rule of thumb as far as the balancer is concerned, is: <strong>Do not
use HTTP session stickiness unless you really have to.</strong> Enabling
@@ -1361,8 +2059,8 @@ that all PHP applications have access to these data. Application
servers such as Websphere can be configured to replicate session data
between nodes.
<p>
-<a name="l28"></a>
-<strong>5.2.2: But if you must..</strong>
+<a name="l51"></a>
+<strong>5.3.2: But if you must..</strong>
<p>
However, if you <strong>must</strong> use session stickiness, then proceed as
follows:
@@ -1393,7 +2091,7 @@ Below is a short example of a configuration.
<pre>
service www {
port 80;
- type stickyhttp;
+ type http;
revivinginterval 15;
dispatchmode byconnections;
@@ -1416,8 +2114,8 @@ Note how the cookie names and values in the directives
<code>stickycookie</code> and <code>addclientheader</code> match. That is obviously a
prerequisite for stickiness.
<p>
-<a name="l29"></a>
-<h3>5.3: Passing the client's IP address</h3>
+<a name="l52"></a>
+<h3>5.4: Passing the client's IP address</h3>
<p>
Since Crossroads just shuttles bytes to and fro, meta-information of
network connections is lost. As far as the back ends are concerned,
@@ -1444,8 +2142,8 @@ header: <code>X-Real-IP</code>, holding the client's IP address.
performance will be hampered -- all passing messages will have to be
unpacked and analyzed.
<p>
-<a name="l30"></a>
-<strong>5.3.1: Sample Crossroads configuration</strong>
+<a name="l53"></a>
+<strong>5.4.1: Sample Crossroads configuration</strong>
<p>
The below sample configuration shows two HTTP back ends that receive
the client's IP address:
@@ -1471,8 +2169,8 @@ service www {
</pre>
<p>
-<a name="l31"></a>
-<strong>5.3.2: Sample Apache configuration</strong>
+<a name="l54"></a>
+<strong>5.4.2: Sample Apache configuration</strong>
<p>
The method by which each back end analyzes the header <code>X-Real-IP</code>
will obviously be different per server implementations. However, a
@@ -1503,8 +2201,8 @@ LogFormat "%{X-Real-IP}i %l %u %t %D \"%r\" %>s %b" common
</pre>
<p>
-<a name="l32"></a>
-<h3>5.4: Debugging network traffic</h3>
+<a name="l55"></a>
+<h3>5.5: Debugging network traffic</h3>
<p>
Incase the traffic between
client and backend
@@ -1627,15 +2325,154 @@ Summarizing, the throughput times of a client-back end connection
analyze the output and to compute round trip times. Such scripts
are not (yet) included in Crossroads.
<p>
-<a name="l33"></a>
-<h3>5.5: Configuration examples</h3>
+<a name="l56"></a>
+<h3>5.6: Limiting Access to Crossroads by Client IP Address</h3>
+<p>
+<a name="l57"></a>
+<strong>5.6.1: General Examples</strong>
+<p>
+The directives <code>allowfrom</code>, <code>denyfrom</code>, <code>allowfile</code> and
+<code>denyfile</code> can be used to instruct Crossroads to specifically allow
+access by using a "whitelist" of IP addresses, or to specifically deny
+access by using a "blacklist". E.g., the following configuration
+allows access to service <code>webproxy</code> only to <em>localhost</em>:
+<p>
+<pre>
+service webproxy {
+ port 8000;
+ allowfrom 127.0.0.1;
+ backend one {
+ .
+ . Back end definitions occur here
+ .
+ }
+ .
+ . Other back ends or other service directives
+ . may occur here
+ .
+}
+</pre>
+
+<p>
+In this example there is a "whitelist" having only one entry: IP
+address 127.0.0.1, or <em>localhost</em>. (Incidentally, the same behaviour
+could be accomplished by stating <em>bindto 127.0.0.1</em>, in which case
+Crossroads would only listen to the local network device.)
+<p>
+In the same vein, the directive <code>allowfrom 127.0.0.1 192.168.1/24</code>
+would allow access to <em>localhost</em> and to all IP addresses that start
+with 192.168.1. The specifier <code>192.168.1/24</code> states that there are
+three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are
+relevant; so that the fourth network byte doesn't matter.
+<p>
+<a name="l58"></a>
+<strong>5.6.2: Using External Files</strong>
+<p>
+The directives <code>allowfile</code> and <code>denyfile</code> allow you to specify IP
+addresses in external files. The Crossroads configuration states
+e.g. <code>allowfile /tmp/allow.txt</code>, and the IP addresses are then in
+<code>/tmp/allow.txt</code>. The format of <code>/tmp/allow.txt</code> is as follows:
+<p>
+<ul>
+ <li> The specifications follow again <em>p.q.r.s/mask</em>, where
+ p, q, r and s are network bytes which can be left out on the
+ right hand side when the mask allows it;
+<p>
+<li> The specifications must be separated by white space
+ (spaces, tabs or newlines).</ul>
+<p>
+E.g., the following is a valid example of an external specification
+file:
+<p>
+<pre>
+127.0.0.1
+192.168.1/24
+10/8
+</pre>
+
+<p>
+When external files are in effect, then the signal <code>SIGHUP</code> (1)
+causes Crossroads to reload the external file. E.g., while Crossroads
+is running, you may edit <code>/tmp/allow.txt</code>, and then issue <code>killall
+-1 crossroads</code>. The new contents of <code>/tmp/allow.txt</code> will be
+reloaded.
+<p>
+<a name="l59"></a>
+<strong>5.6.3: Mixing Directives</strong>
+<p>
+Crossroads allows to mix all directives in one service
+description. However, some mixes are less meaningful than others. It's
+up to you to take this into account.
+<p>
+The following rules apply:
+<p>
+<ul>
+ <li> Blacklisting and whitelisting can be used together. When
+ combined, the blacklist will always be interpreted
+ first. E.g., consider the following directives:
+<p>
+<pre>
+allowfrom 192.168.1/24
+denyfrom 192.168.1.100
+</pre>
+
+<p>
+Given the fact that the deny list is checked first, client
+ 192.168.1.100 won't be able to access Crossroads. Then the
+ allow list will be checked, stating that all clients whose IP
+ address starts with 192.168.1 may connect. The effect will be
+ that e.g., client 192.168.1.1 may connect, 192.168.1.2 may
+ connect too, 192.168.1.100 will be blocked, and 10.1.1.1 will
+ be blocked as well.
+<p>
+Now consider the following directives:
+<p>
+<pre>
+allowfrom 192.168.1.100 127.0.0.1
+denyfrom 192.168.1/24
+</pre>
+
+<p>
+This will first of all deny access to all IP addresses that
+ start with 192.168.1. So the rule that allows 192.168.1.100
+ won't ever be effective. The net result will be that access
+ will be granted to 127.0.0.1, and IP addresses that don't
+ match 192.168.1/24.
+<p>
+<li> Blacklisting or whitelisting can be left out.
+ A list is considered empty when no appropriate directives
+ occur in <code>/etc/crossroads.conf</code>, or when the directive
+ points to an empty or non-existent external file.
+<p>
+<li> Using <code>*from</code> and <code>*file</code> statements is allowed, but
+ doesn't make sense. E.g., the following configuration sample
+ is such a case:
+<p>
+<pre>
+allowfrom 127.0.0.1 192.168.1/24
+allowfile /tmp/allow.txt
+</pre>
+
+<p>
+There is a technical reason for this. Once Crossroads
+ processes the <code>allowfile</code> directive, then the whole
+ whitelist is cleared (thereby removing the entries 127.0.0.1
+ and 192.168.1/24), and new entries are reloaded from the
+ file. The net result is that the <code>allowfrom</code> specification
+ is overruled.
+<p>
+Crossroads doesn't check for such configurations, which are
+ syntactially correct, but make no semantic sense.</ul>
+<p>
+<a name="l60"></a>
+<h3>5.7: Configuration examples</h3>
<p>
As a general hint, use <code>crossroads sampleconf</code> to view the most
up-to-date examples of configurations. The description below shows a
few examples too.
<p>
-<a name="l34"></a>
-<strong>5.5.1: A load balancer for three webserver back ends</strong>
+<a name="l61"></a>
+<strong>5.7.1: A load balancer for three webserver back ends</strong>
<p>
The following configuration example binds crossroads to port 80 of the
current server, and distributes the load over three back ends. This
@@ -1763,8 +2600,8 @@ service www {
</pre>
<p>
-<a name="l35"></a>
-<strong>5.5.2: An HTTP forwarder when travelling</strong>
+<a name="l62"></a>
+<strong>5.7.2: An HTTP forwarder when travelling</strong>
<p>
As another example, here's my <code>crossroads.conf</code> that I use on my
Unix laptop. The problem that I face is that I need many HTTP proxy
@@ -1852,8 +2689,8 @@ and <code>LocalSquid</code> are both active, then <code>crossroads tell httpprox
sshtunnel down</code> will 'take down' the back end <code>SshTunnel</code> -- and
will automatically cause crossroads to switch to <code>LocalSquid</code>.
<p>
-<a name="l36"></a>
-<strong>5.5.3: SSH login with enforced idle logout</strong>
+<a name="l63"></a>
+<strong>5.7.3: SSH login with enforced idle logout</strong>
<p>
The following example shows how crossroads 'throttles' SSH
logins. Connections are accepted on port
@@ -1878,13 +2715,13 @@ service Ssh {
</pre>
<p>
-<a name="l37"></a>
+<a name="l64"></a>
<h2>6: Benchmarking</h2>
<a name="benchmarking"></a>This section shows how crossroads affects the
transmitting of HTML data when used as an intermediate 'station'
through which all data travels.
<p>
-<a name="l38"></a>
+<a name="l65"></a>
<h3>6.1: Benchmark 1: Accessing a proxy via crossroads or directly</h3>
<p>
The benchmark was run on a system where the following was varied:
@@ -1912,7 +2749,7 @@ service HttpProxy {
</pre>
<p>
-<a name="l39"></a>
+<a name="l66"></a>
<strong>6.1.1: Results</strong>
<p>
The results of this test are that crossroads causes a negligible
@@ -1935,7 +2772,7 @@ sys 0m0.230s
</pre>
<p>
-<a name="l40"></a>
+<a name="l67"></a>
<strong>6.1.2: Discussion</strong>
<p>
The above shown results are quite favorable to crossroads. However,
@@ -1967,7 +2804,7 @@ seldom in the real world:
back end). Again, this processing time will weigh much heavier
than the multiple read/writes.</ul>
<p>
-<a name="l41"></a>
+<a name="l68"></a>
<h3>6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</h3>
<p>
LVS is a kernel-based balancer that acts like a masquerading
@@ -1981,7 +2818,7 @@ LVS isn't aware of downtime of back ends (unless one implements an
external heartbeat). Also, crossroads offers more complex balancing
than LVS.
<p>
-<a name="l42"></a>
+<a name="l69"></a>
<strong>6.2.1: Environment</strong>
<p>
On the balancer, LVS was run on port 80, its forwarding set up for two
@@ -2012,7 +2849,7 @@ service http {
</pre>
<p>
-<a name="l43"></a>
+<a name="l70"></a>
<strong>6.2.2: Tests and results</strong>
<p>
In the first test, ports 80 and 81 on the balancer were 'bombed' with
@@ -2091,9 +2928,9 @@ are shown in the below table:
Again, the results show that crossroads performs just as effectively
as LVS, even with large data chunks!
<p>
-<a name="l44"></a>
+<a name="l71"></a>
<h2>7: Compiling and Installing</h2>
-<a name="compiling"></a><a name="l45"></a>
+<a name="compiling"></a><a name="l72"></a>
<h3>7.1: Prerequisites</h3>
<p>
The creation of crossroads requires:
@@ -2110,7 +2947,7 @@ The creation of crossroads requires:
Basically a Linux or Apple MacOSX box will do nicely. To compile and install
crossroads, follow these steps.
<p>
-<a name="l46"></a>
+<a name="l73"></a>
<h3>7.2: Compiling and installing</h3>
<p>
<ul>
@@ -2168,7 +3005,7 @@ crossroads, follow these steps.
<p>
</ul>
<p>
-<a name="l47"></a>
+<a name="l74"></a>
<h3>7.3: Configuring crossroads</h3>
<p>
Now that the binary is available on your system, you need to create a
@@ -2217,13 +3054,13 @@ which crossroads daemons are running. Finally, the tailing of
<code>/var/log/messages</code> shows what's going on -- especially if you have
<code>verbosity true</code> statements in the configuration.
<p>
-<a name="l48"></a>
+<a name="l75"></a>
<h3>7.4: A boot script</h3>
<p>
Finally, you may want to create a boot-time startup script. The exact
procedure depends on the used Unix flavor.
<p>
-<a name="l49"></a>
+<a name="l76"></a>
<strong>7.4.1: SysV Style Startup</strong>
<p>
On SysV style systems, there's a startup script directory
@@ -2265,7 +3102,7 @@ If your runlevel is 5, then the right <code>cd</code> command is to
<code>/etc/rc.d/rc5.d</code>. Alternatively, you can create the
symlinks in both runlevel directories.</ul>
<p>
-<a name="l50"></a>
+<a name="l77"></a>
<strong>7.4.2: BSD Style Startup</strong>
<p>
On BSD style systems, daemons are booted directly from <code>/etc/rc</code> and
diff --git a/doc/crossroads.man b/doc/crossroads.man
@@ -1,6 +1,6 @@
-.TH "Crossroads 1\&.17" "2005, 2006, ff\&."
+.TH "Crossroads 1\&.23" "2005, 2006, ff\&."
.PP
-.SH "Crossroads 1\&.17"
+.SH "Crossroads 1\&.23"
.SH "Karel Kubat"
.SH "e-tunity"
.SH "2005, 2006, ff\&."
@@ -184,7 +184,25 @@ controlled manner, without any client noticing it\&.
.PP
-.SH "1\&.4: Porting issues for pre-0\&.26 installations"
+.SH "1\&.4: Porting issues for pre-1\&.21 installations"
+
+.PP
+As of version 1\&.21, the event-hook directives \f(CWonsuccess\fP and
+\f(CWonfailure\fP no longer exists\&.
+.PP
+.IP o
+Please replace \f(CWonsuccess\fP by \f(CWonstart\fP;
+.IP o
+Please replace \f(CWonfailure\fP bu \f(CWonfail\fP;
+.IP o
+Note that there is a new hook \f(CWonend\fP\&.
+.PP
+The commands that are run via \f(CWonstart\fP, \f(CWonend\fP or \f(CWonfail\fP
+ are subject to format expansion; e\&.g\&., \f(CW%1w\fP is expanded to the
+ weight of the first back end, etc\&.\&. See section ?? for details\&.
+.PP
+
+.SH "1\&.5: Porting issues for pre-0\&.26 installations"
.PP
As of version 0\&.26 the syntax of the configuration file has
@@ -205,7 +223,7 @@ multiple TCP connections, and the term
TCP connection\&.)
.PP
-.SH "1\&.5: Porting issues for pre-1\&.08 installations"
+.SH "1\&.6: Porting issues for pre-1\&.08 installations"
.PP
As of version 1\&.08, the following directives no longer are
@@ -411,7 +429,19 @@ Now start \f(CWcrossroads\fP with the flag \f(CW-l7\fP\&.
.IP
.IP o
Finally, monitor \f(CW/var/log/crossroads\&.log\fP for Crossroads\&'
-messages\&.
+messages\&.
+.PP
+
+.SH "3\&.3: Reloading Configurations"
+
+.PP
+Crossroads doesn\&'t support the reloading of a configuration while
+running (such as other programs, e\&.g\&. Apache do)\&. There are various
+technical reasons for this\&.
+.PP
+However, external lists of allowed or denied IP addresses can be
+reloaded by sending a signal -1 (\f(CWSIGHUP\fP) to Crossroads\&. See
+section ?? for the details\&.
.PP
.SH "4: The configuration"
@@ -513,78 +543,83 @@ identifying names differ\&. The following list shows possible
statements\&. Each statement must end with a semicolon, except for the
\f(CWbackend\fP statement, which has is own block (more on this later)\&.
.PP
-.IP "The type statement"
-defines how crossroads handles the stated
+
+.SH "4\&.2\&.1: type - Defining the service type"
+.IP "Description:"
+The \f(CWtype\fP statement defines how crossroads handles the stated
service\&. There are currently two types: \f(CWany\fP and
\f(CWhttp\fP\&. The type \f(CWany\fP means that crossroads doesn\&'t
interpret the contents of a TCP stream, but only distributes streams
over back ends\&. The type \f(CWhttp\fP means that crossroads has to
analyze what\&'s in the messages, does magical HTTP header tricks, and
so on -- all to ensure that multiple connections are treated as one
-session, or that the back end is notified of the client\&'s IP address\&.
+session, or that the back end is notified of the client\&'s IP
+address\&.
.IP
Unless you really need such special features, use the type \f(CWany\fP (the
default), even for HTTP protocols\&.
-.IP
-.IP o
-Syntax: \f(CWtype\fP \fItypespecifier\fP \f(CW;\fP
-.IP o
-Where \fItypespecifier\fP is \f(CWany\fP or \f(CWhttp\fP
-.IP o
-Default: \f(CWany\fP
-.IP
-.IP "The port statement"
-defines to which TCP port a service
+.IP "Syntax:"
+\f(CWtype\fP \fIspecifier\fP, where \fIspecifier\fP is \f(CWany\fP or
+\f(CWhttp\fP
+.IP "Default:"
+\f(CWany\fP
+
+.PP
+
+.SH "4\&.2\&.2: port - Specifying the listen port"
+.IP "Description:"
+The \f(CWport\fP statement defines to which TCP port a service
\&'listens\&'\&. E\&.g\&. \f(CWport 8000\fP says that this service will accept
connections on port 8000\&.
-.IP
-.IP o
-Syntax: \f(CWport\fP \fInumber\fP \f(CW;\fP
-.IP o
+.IP "Syntax:"
+\f(CWport\fP \fInumber\fP
+.IP "Default:"
There is no default\&. This is a required setting\&.
-.IP
-.IP "The bindto statement"
-is used in situations where crossroads
+
+.PP
+
+.SH "4\&.2\&.3: bindto - Binding to a specific IP address"
+.IP "Description:"
+The \f(CWbindto\fP statement is used in situations where crossroads
should only listen to the stated port at a given IP address\&. E\&.g\&.,
\f(CWbindto 127\&.0\&.0\&.1\fP causes crossroads to \&'bind\&' the service only to
the local IP address\&. Network connections from other hosts won\&'t be
serviced\&. By default, crossroads binds a service to all presently
active IP addresses at the invoking host\&.
-.IP
-.IP o
-Syntax: \f(CWbindto\fP \fIip-address\fP \f(CW;\fP
-.IP o
-where \fIip-address\fP is a numeric IP address, such as
-\f(CW192\&.168\&.1\&.45\fP, or the keyword \f(CWany\fP
-.IP o
-Default: \f(CWany\fP
-.IP
-.IP "Verbosity statements"
-come in two forms: \f(CWverbosity on\fP or
+.IP "Syntax:"
+\f(CWbindto\fP \fIaddress\fP, where \fIaddress\fP is a numeric IP
+address, such as 127\&.0\&.0\&.1, or the keyword \f(CWany\fP\&.
+.IP "Default:"
+\f(CWany\fP
+
+.PP
+
+.SH "4\&.2\&.4: verbosity - Controlling debug output"
+.IP "Description:"
+Verbosity statements come in two forms: \f(CWverbosity on\fP or
\f(CWverbosity off\fP\&. When \&'on\&', log messages to \f(CW/var/log/messages\fP
are generated that show what\&'s going on\&.\e (Actually, the
messages go to \f(CWsyslog(3)\fP, using facility \f(CWLOG_DAEMON\fP and
priority \f(CWLOG_INFO\fP\&. In most (Linux) cases this will mean: output to
\f(CW/var/log/messages\fP\&. On Mac OSX the messages go to
\f(CW/var/log/system\&.log\fP\&.) The keyword \f(CWverbose\fP is an alias for
-\f(CWverbosity\fP\&.
-.IP
-.IP o
-Syntax: \f(CWverbosity\fP \fIsetting\fP \f(CW;\fP
-.IP o
-Or: \f(CWverbose\fP \fIsetting\fP \f(CW;\fP
-.IP o
-where \fIsetting\fP is \f(CWtrue\fP, \f(CWyes\fP or \f(CWon\fP to turn
+\f(CWverbosity\fP\&.
+.IP "Syntax:"
+\f(CWverbosity\fP \fIsetting\fP or \f(CWverbose\fP \fIsetting\fP, where
+\fIsetting\fP is \f(CWtrue\fP, \f(CWyes\fP or \f(CWon\fP to turn
verbosity on; or \f(CWfalse\fP, \f(CWno\fP, \f(CWoff\fP to turn it off\&.
-.IP o
-Default: \f(CWoff\fP\&.
-.IP
-.IP "The dispatch mode"
- controls how crossroads selects a back end from
-a list of active back ends\&. The below text shows the bare
-syntax\&. See section ?? for a textual explanation\&.
+.IP "Default:"
+\f(CWoff\fP
+
+.PP
+
+.SH "4\&.2\&.5: dispatchmode - How are back ends selected"
+.IP "Description:"
+ The dispatch mode controls how crossroads selects a back end from
+ a list of active back ends\&. The below text shows the bare
+ syntax\&. See section ?? for a textual explanation\&.
.IP
-The syntax is:
+The settings can be:
.IP
.IP o
\f(CWdispatchmode roundrobin\fP: Simply the \&'next in line\&' is
@@ -630,6 +665,18 @@ for e\&.g\&. database connections\&.
every time, unless it\&'s unavailable\&. In that case the second
is taken, and so on\&.
.IP
+.IP o
+\f(CWdispatchmode externalhandler\fP \fIprogram arguments\fP:
+This is a special mode, where an external program is delegated
+the responsibility to say which back end should be used
+next\&. In this case, Crossroads will call the external program,
+and this will of course be slower than one of the \&'built-in\&'
+dispatch modes\&. However, this is the ultimate escape when
+custom-made dispatch modes are needed\&.
+.IP
+The dispatch mode that uses an \f(CWexternalhandler\fP is
+ discussed separately in section ??\&.
+.IP
The selection algorithm is only used when clients are serviced that
aren\&'t part of a sticky HTTP session\&. This is the case during:
.IP
@@ -647,9 +694,18 @@ the fact that crossroads doesn\&'t know (and doesn\&'t care) how to
estimate load from a network traffic stream, you have to choose an
appropriate dispatch mode to optimize load balancing\&. In most cases,
\f(CWroundrobin\fP or \f(CWbyconnections\fP will do the job just fine\&.
-.IP
-.IP "A reviving interval definition"
-is needed when crossroads
+.IP "Syntax:"
+\f(CWdispatchmode\fP \fImode\fP (see above for the modes), optionally
+followed by \f(CWover\fP \fInumber\fP, or when the \fImode\fP is
+\f(CWexternalhandler\fP, followed by \fIprogram\fP\&.
+.IP "Default:"
+\f(CWroundrobin\fP
+
+.PP
+
+.SH "4\&.2\&.6: revivinginterval - Back end wakeup calls"
+.IP "Description:"
+A reviving interval definition is needed when crossroads
determines that a back end is temporarily unavailable\&. This will
happen when:
.IP
@@ -664,14 +720,17 @@ reviving interval is given, crossroads will check each 10 seconds
whether unavailable back ends have woken up yet\&. A back end is
considered awake when a network connection to that back end can
succesfully be established\&.
-.IP
-.IP o
-Syntax: \f(CWrevivinginterval\fP \fInumber\fP \f(CW;\fP
-.IP o
-Default: 0, meaning no revivals will occur\&.
-.IP
-.IP "The maximum number of connections"
-is specified using
+.IP "Syntax:"
+\f(CWrevivinginterval\fP \fInumber\fP, where the number is the interval
+in seconds\&.
+.IP "Default:"
+0 (no wakeup calls)
+
+.PP
+
+.SH "4\&.2\&.7: maxconnections - Limiting concurrent clients at service level"
+.IP "Description:"
+The maximum number of connections is specified using
\f(CWmaxconnections\fP\&. There is one argument; the number of concurrent
established connections that may be active within one service\&.
.IP
@@ -679,15 +738,17 @@ established connections that may be active within one service\&.
Service (DOS) attacks\&. Without a limit, numerous network connections
may spawn so many server instances, that the service ultimately breaks
down and becomes unavailable\&.
-.IP
-.IP o
-Syntax: \f(CWmaxconnections\fP \fInumber\fP \f(CW;\fP
-.IP o
-Default: 0, meaning that all client connections will be
-accepted\&.
-.IP
-.IP "The TCP back log size"
-is a number that controls how many
+.IP "Syntax:"
+\f(CWmaxconnections\fP \fInumber\fP, where the number specifies the
+maximum of concurrent connections to the service\&.
+.IP "Default:"
+0, meaning that all connections will be accepted\&.
+
+.PP
+
+.SH "4\&.2\&.8: backlog - The TCP Back Log size"
+.IP "Description:"
+The TCP back log size is a number that controls how many
\&'waiting\&' network connections may be queued, before a client simply
cannot connect\&. The syntax is e\&.g\&. \f(CWbacklog 5\fP to cause crossroads
to have 5 waiting connections for 1 active connection\&.
@@ -695,15 +756,17 @@ The backlog queue shouldn\&'t be too
high, or clients will experience timeouts before they can actually
connect\&. The queue shouldn\&'t be too small either, because clients
would be simply rejected\&. Your mileage may vary\&.
-.IP
-.IP o
-Syntax: \f(CWbacklog\fP \fInumber\fP \f(CW;\fP
-.IP o
-Default: zero, which takes the operating system\&'s default
+.IP "Syntax:"
+\f(CWbacklog\fP \fInumber\fP
+.IP "Default:"
+0, which takes the operating system\&'s default
value for socket back log size\&.
-.IP
-.IP "Reporting based: the shared memory key\&."
-Different crossroad
+
+.PP
+
+.SH "4\&.2\&.9: shmkey - Shared Memory Access"
+.IP "Description:"
+Different Crossroads
invocations must \&'know\&' of each others activity\&. E\&.g, \f(CWcrossroad
status\fP must be able to get to the actual state information of all
running services\&. This is internally implemented through shared
@@ -714,30 +777,115 @@ service port and bitwise or-ed with a magic number\&. In situations
where this conflicts with existing keys (of other programs, having
their own keys), you may supply a chosen value\&.
.IP
-The syntax is e\&.g\&. \f(CWshmkey 123456\fP\&. The actual key value doesn\&'t
-matter much, as long as it\&'s unique and as long as each invocation of
-crossroads uses it\&.
+The actual key value doesn\&'t matter much, as long as it\&'s unique
+and as long as each invocation of crossroads uses it\&.
+.IP "Syntax:"
+\f(CWshmkey\fP \fInumber\fP
+.IP "Default:"
+0, which means that crossroads will \&'guess\&' its
+own key, based on TCP port and a magic number\&.
+
+.PP
+
+.SH "4\&.2\&.10: allow* and deny* - Allowing or denying connections"
+.IP "Description:"
+Crossroads can allow or deny
+connections based on the IP address of a client\&. There are four
+directives that are relevant: \f(CWallowfrom\fP, \f(CWallowfile\fP,
+\f(CWdenyfrom\fP and \f(CWdenyfile\fP\&. When using \f(CWallowfrom\fP and
+\f(CWdenyfrom\fP then the IP addresses to allow or deny connections are
+stated in \f(CW/etc/crossroads\&.conf\fP\&.
+.IP
+When \f(CWallow*\fP directives are used, then all connections are denied
+unless they match the stated allowed IP\&'s\&. When \f(CWdeny*\fP directives
+are used, then all connections are allowed unless they match the
+stated disallowed IP\&'s\&. When denying and allowing is both used,
+then the Crossroads checks the deny list first\&.
+.IP
+The statements \f(CWallowfrom\fP and \f(CWdenyfrom\fP are followed by a
+list of filter specifications\&. The statements \f(CWallowfile\fP and
+\f(CWdenyfile\fP are followed by a filename; Crossroads will read
+filter specifications from those external files\&. In both cases,
+Crossroads obtains filter specifications and places them in its
+lists of allowed or denied IP addresses\&. The difference between
+specifying filters in \f(CW/etc/crossroads\&.conf\fP or in external
+files, is that Crossroads will reload the external files when it
+receives signal 1 (\f(CWSIGHUP\fP), as in \f(CWkillall -1 crossroads\fP\&.
+.IP
+The filter specifications must obey the following syntax: it
+consists of up to
+four numbers ranging from 0 to 255 and separated by a decimal
+sign\&. Optionally a slash follows, with a bitmask which is also a
+decimal number\&.
+.IP
+This is probably best explained by a few examples:
.IP
.IP o
-Syntax: \f(CWshmkey\fP \fInumber\fP \f(CW;\fP
+\f(CWallowfrom 10/8;\fP will allow connections from
+\f(CW10\&.*\&.*\&.*\fP (a full Class A network)\&. The mask \f(CW/8\fP means
+that the first 8 bits of the number (ie\&., only the \f(CW10\fP) are
+significant\&. On the last 3 positions of the IP address, all
+numbers are allowed\&. Given this directive, client connections
+from e\&.g\&. 10\&.1\&.1\&.1 and 10\&.2\&.3\&.4 will be allowed\&.
+.IP
.IP o
-Default: 0, which means that crossroads will \&'guess\&' its
-own key, based on TCP port and a magic number\&.
+\f(CWallowfrom 10\&.3/16;\fP will allow all IP addresses that
+start with \f(CW10\&.3\fP\&.
.IP
-.IP "Connection timeouts:"
-Sometimes, clients simply won\&'t close a network
-connection which leads to unnecessary resource usage\&. To avoid this,
-one might state e\&.g\&. \f(CWconnectiontimeout 300\fP\&. This instructs crossroads to
-consider a connection where nothing has happened for 300 seconds as
-\&'finished\&'\&. Crossroads will terminate the connection when this timeout
-is exceeded\&.
+.IP o
+\f(CWallowfrom 10\&.3\&.1/16;\fP is the same as above\&. The third
+byte of the IP address is superfluous because the netmask
+specifies that only the first 16 bits (2 numbers) are taken
+into account\&.
.IP
.IP o
-Syntax: \f(CWconnectiontimeout\fP \fInumber\fP \f(CW;\fP
+\f(CWallowfrom 10\&.3\&.1\&.15;\fP allows traffic from only the
+specified IP address\&. There is no bitmask; all four numbers
+are relevant\&.
+.IP
.IP o
-Default: 0, meaning that crossroads will not try to
-determine timeouts\&.
+\f(CWallowfrom 10\&.3\&.1\&.15 10\&.2/16;\fP allows traffic from one
+IP address \f(CW10\&.3\&.1\&.15\fP or from a complete Class B network
+\f(CW10\&.2\&.*\&.*\fP
.IP
+.IP o
+\f(CWallowfile /tmp/myfile\&.txt;\fP in combination with a file
+\f(CW/tmp/myfile\&.txt\fP, with the contents \f(CW10\&.3\&.1\&.15 10\&.2/16\fP,
+is the same as above\&.
+.IP "Syntax:"
+.IP o
+\f(CWallowfrom\fP \fIfilter-specificication(s)\fP
+.IP o
+\f(CWdenyfrom\fP \fIfilter-specificication(s)\fP
+.IP o
+\f(CWallowfile\fP \fIfilename\fP
+.IP o
+\f(CWdenyfile\fP \fIfilename\fP
+.IP "Default:"
+In absence of these statements, all client IP\&'s are accepted\&.
+
+.PP
+
+.SH "4\&.2\&.11: useraccount - Limiting the effective ID of external processes"
+.IP "Description:"
+Using the directive \f(CWuseraccount\fP, the effective user and group
+ID can be restricted\&. This comes into effect when Crossroads runs
+external commands, such as:
+.IP o
+Hooks for \f(CWonstart\fP, \f(CWonend\fP or \f(CWonfail\fP;
+.IP o
+External dispatchers, when \f(CWdispatchmode
+externalhandler\fP is in effect\&.
+Once a user name for external commands is specified, Crossroads
+assumes the associated user ID and group ID before running those
+commands\&.
+.IP "Syntax:"
+\f(CWuseraccount\fP \fIusername\fP
+.IP "Default:"
+None; when unspecified, external commands are run with the
+ID that was in effect when Crossroads was started\&.
+
+.PP
.SH "4\&.3: Backend definitions"
@@ -770,91 +918,69 @@ balancing and fail over) as long as the backend names differ\&.
The statements in the backend definition blocks are described in the
following sections\&.
.PP
-
-.SH "4\&.3\&.1: General Backend Directives"
-
-.PP
-The following directives are used in all types of services (\f(CWany\fP or
-\f(CWhttp\fP)\&. HTTP-specific directives are shown in section ??\&.
+Some directives (\f(CWstickycookie\fP etc\&.) only have effect when
+Crossroads treats the network traffic as a stream of HTTP messages;
+i\&.e\&., when the service is declared with \f(CWtype http\fP\&. Incase of
+\f(CWtype any\fP, the HTTP-specific directives have no effect\&.
.PP
-.IP "Server:"
+
+.SH "4\&.3\&.1: server - Specifying the back end address"
+.IP "Description:"
Each back end must be identified by the network name
(server name) where it is located\&. For example: \f(CWserver
10\&.1\&.1\&.23\fP, or \f(CWserver web\&.mydomain\&.org\fP\&. A TCP port specifier
can follow the server name, as in \f(CWserver web\&.mydomain\&.org:80\fP\&.
-.IP
-.IP o
-Syntax: \f(CWserver\fP \fIservername\fP \f(CW;\fP
+.IP "Syntax:"
.IP o
-Or: \f(CWserver\fP \fIservername\fP\f(CW:\fP\fIport\fP \f(CW;\fP
+\f(CWserver\fP \fIservername\fP, where \fIservername\fP is a
+network name or IP address;
.IP o
+\f(CWserver\fP \fIservername:port\fP
+.IP "Default:"
There is no default\&. This is a required setting\&.
-.IP
-.IP "Port:"
-When the \f(CWserver\fP specifier doesn\&'t include a TCP
-port, then this statement is used to define the port at which the
-back end expects its traffic\&. There is one argument, the (numeric)
-port number\&.
-.IP
-.IP o
-Syntax: \f(CWport\fP \fInumber\fP \f(CW;\fP
-.IP o
-There is no default\&. The port must be defined either in
-the \f(CWserver\fP setting or using the \f(CWport\fP specifier\&.
-.IP
-.IP "Verbosity:"
+
+.PP
+
+.SH "4\&.3\&.2: verbosity - Controlling verbosity at the back end level"
+.IP "Description:"
Similar to \f(CWservice\fP specifications, a
\f(CWbackend\fP can have its own verbosity (\f(CWon\fP or \f(CWoff\fP)\&. When
-\f(CWon\fP, traffic to and fro this back end is reported\&.
-.IP
-.IP o
-Syntax: \f(CWverbosity\fP \fIsetting\fP \f(CW;\fP
-.IP o
-Or: \f(CWverbose\fP \fIsetting\fP \f(CW;\fP
-.IP o
-where \fIsetting\fP is \f(CWtrue\fP, \f(CWyes\fP or \f(CWon\fP to turn
-verbosity on; or \f(CWfalse\fP, \f(CWno\fP, \f(CWoff\fP to turn it off\&.
-.IP o
-Default: \f(CWoff\fP\&.
-.IP
-.IP "Maxconnections:"
-This setting states how many concurrent connections
-a back end connection may accept\&. Note that there is also a
-\f(CWmaxconnections\fP statement for the overall service description\&.
-.IP
-The difference is that a \f(CWmaxconnections\fP statement at the level of
-a service description avoids too many hits from the outside (DOS
-prevention)\&. A \f(CWmaxconnections\fP statement at the level of a back end
-description makes sure that this particular back end doesn\&'t get
-overloaded\&.
-.IP
-.IP o
-Syntax: \f(CWmaxconnections\fP \fInumber\fP \f(CW;\fP
+\f(CWon\fP, traffic to and fro this back end is reported\&.
+.IP "Syntax:"
.IP o
-where \fInumber\fP is the maximum number of concurrent
-client connections\&.
+\f(CWverbosity\fP \fIsetting\fP, or
.IP o
-Default: 0, meaning that there is no limit\&.
-.IP
-.IP "Weight:"
-To influence how backends are selected by size or by
-duration, a backend can specify its \&'weight\&' in the process\&. The
-higher the weight, the less likely a back end will be chosen\&. The
-default is 1\&.
+\f(CWverbose\fP \fIsetting\fP, where \fIsetting\fP is \f(CWtrue\fP,
+\f(CWyes\fP or \f(CWon\fP, or \f(CWfalse\fP, \f(CWno\fP, \f(CWoff\fP to turn it
+off\&.
+.IP "Default:"
+\f(CWoff\fP
+
+.PP
+
+.SH "4\&.3\&.3: weight - When a back end is more equal than others"
+.IP "Description:"
+To influence how backends are selected, a backend can specify its
+\&'weight\&' in the process\&. The higher the weight, the less likely a
+back end will be chosen\&. The default is 1\&.
.IP
The weighing mechanism only applies to the dispatch modes
\f(CWrandom\fP, \f(CWbyconnections\fP, \f(CWbysize\fP and \f(CWbyduration\fP\&.
The weight is in fact a penalty factor\&. E\&.g\&., if backend A has
\f(CWweight 2\fP and backend B has \f(CWweight 1\fP, then backend B will
be selected all the time, until its usage parameter is twice as
-large as the parameter of A\&. Think of it as a \&'sluggishness\&' statement\&.
-.IP
-.IP o
-Syntax: \f(CWweight\fP \fInumber\fP \f(CW;\fP
-.IP o
-Default: 1
-.IP
-.IP "Decay:"
+large as the parameter of A\&. Think of it as a \&'sluggishness\&'
+statement\&.
+.IP "Syntax:"
+\f(CWweight\fP \fInumber\fP; the higher the number, the more \&'sluggish\&'
+a back end is
+.IP "Default:"
+1; all back ends have equal weight\&.
+
+.PP
+
+.SH "4\&.3\&.4: decay - Levelling out activity of a back end"
+.IP "Description:"
To make sure that a \&'spike\&' of activity doesn\&'t
influence the perceived load of a back end forever, you may
specify a certain decay\&. E\&.g, the statement \f(CWdecay 10\fP makes
@@ -868,39 +994,89 @@ of the transferred bytes and the connection duration are updated
using the actual number of bytes and actual duration\&. However,
when a different back end is hit, then the usage data are
decreased by the specified decay\&.
-.IP
+.IP "Syntax:"
+\f(CWdecay\fP \fInumber\fP, where \fInumber\fP is a percentage that
+decreases the back end usage data when other back ends are
+hit\&.
+.IP "Default:"
+0, meaning that no decay is applied to usage statistics\&.
+
+.PP
+
+.SH "4\&.3\&.5: onstart, onend, onfail - Action Hooks"
+.IP "Description:"
+The three directives \f(CWonstart\fP, \f(CWonend\fP and \f(CWonfail\fP can be
+specified to start system commands (external programs) when a
+connection to a back end starts, fails or ends:
.IP o
-Syntax: \f(CWdecay\fP \fInumber\fP \f(CW;\fP
+\f(CWonstart\fP commands will be run when Crossroads
+successfully connects to a back end, and starts servicing;
.IP o
-where \fInumber\fP is a percentage that decreases the back
-data when other back ends are hit
+\f(CWonend\fP commands will be run when a (previously
+established) connection stops;
.IP o
-Default: 0, meaning that no decay is applied to usage
-statistics\&.
+\f(CWonfail\fP commands will be run when Crossroads tries to
+contact a back end to serve a client, but the back end can\&'t
+be reached\&.
.IP
-.IP "Event triggers:"
-As special \&'hooks\&' for actions, two triggers
-are available: \f(CWonfailure\fP and \f(CWonsuccess\fP\&. The argument to
-the triggers is a system command that is executed when a connection
-with the back end either fails or succeeds\&.
+The format is always \f(CWon\fP\fItype\fP \fIcommand\fP\&. The \fIcommand\fP
+is an external program, optionally followed by arguments\&. The
+command is expanded according to the following table:
.IP
.IP o
-Syntax: \f(CWonfailure\fP \fIcommandline\fP \f(CW;\fP and
-\f(CWonsuccess\fP \fIcommandline\fP \f(CW;\fP
+\f(CW%a\fP is the availability of the current back end, when
+a current back end is established;
.IP o
-There is no default\&.
-.IP
-.IP "Debugging and Performance aids:"
-Two directives are available
-to log network traffic to files\&. They are \f(CWtrafficlog\fP and
-\f(CWthroughputlog\fP\&.
+\f(CW%1a\fP is the availability of the first back end (0 when
+unavailable, 1 if available); \f(CW%2a\fP is the availability of
+the second back end, and so on;
+.IP o
+\f(CW%b\fP is the name of the current back end, when one is
+established;
+.IP o
+\f(CW%1b\fP is the name of the first back end, \f(CW%2b\fP of the
+second back end, and so on;
+.IP o
+\f(CW%e\fP is the count of seconds since start of epoch
+(January 1st 1970 GMT);
+.IP o
+\f(CW%r\fP is the IP address of the client that requests a
+connection and for whom the external dispatcher should compute
+a back end;
+.IP o
+\f(CW%s\fP is the name of the current service that the client
+connected to;
+.IP o
+\f(CW%t\fP is the current local time in ASCII format, in
+\fIYYYY-MM-DD/hhh:mm:ss\fP;
+.IP o
+\f(CW%T\fP is the current GMT time in ASCIII format;
+.IP o
+\f(CW%v\fP is the Crossroads version;
+.IP o
+Any other chararacter following a \f(CW%\fP sign is taken
+literally; e\&.g\&. \f(CW%z\fP is just a z\&.
.IP
+.IP "Syntax:"
+.IP o
+\f(CWonstart\fP \fIcommandline\fP
.IP o
-Syntax: \f(CWtrafficlog\fP \fIfilename\fP \f(CW;\fP
+\f(CWonend\fP \fIcommandline\fP
.IP o
-And: \f(CWthroughputlog\fP \fIfilename\fP \f(CW;\fP
+\f(CWonfail\fP \fIcommandline\fP
.IP o
-Default: none
+\f(CWonsuccess\fP \fIcommandline\fP
+.IP "Default:"
+There is no default\&. Normally no external programs are run upon
+connection, success or failure of a back end\&.
+
+.PP
+
+.SH "4\&.3\&.6: trafficlog and throughputlog - Debugging and Performance Aids"
+.IP "Description:"
+Two directives are available
+to log network traffic to files\&. They are \f(CWtrafficlog\fP and
+\f(CWthroughputlog\fP\&.
.IP
The \f(CWtrafficlog\fP statement causes all traffic to be logged in
hexadecimal format\&. Each line is prefixed by \f(CWB\fP or \f(CWC\fP,
@@ -909,20 +1085,18 @@ end or from the client\&.
.IP
The \f(CWthroughputlog\fP statement writes shorthand transmissions to
its log, accompanied by timings\&.
-.IP
-
-.SH "4\&.3\&.2: HTTP-related Backend Directives"
+.IP "Syntax:"
+.IP o
+\f(CWtrafficlog\fP \fIfilename\fP
+.IP o
+\f(CWthroughputlog\fP \fIfilename\fP
+.IP "Default:"
+none
.PP
-The following directives are specific for HTTP-type services; i\&.e\&.,
-services with a specification \f(CWtype http\fP\&.
-.PP
-It is inevitable that when Crossroads handles services of \f(CWtype
-http\fP, more processing is necessary\&. Crossroads has to unpack the TCP
-payload in order to do its header magic; which leads to performance
-impact\&.
-.PP
-.IP "Session Stickiness:"
+
+.SH "4\&.3\&.7: stickycookie - Back end selection with an HTTP cookie"
+.IP "Description:"
The directive \f(CWstickycookie\fP \fIvalue\fP
causes Crossroads to unpack clients\&' requests, to check for
\fIvalue\fP in the cookies\&. When found, the message is routed to the
@@ -955,9 +1129,16 @@ back end can insert such a cookie into the HTTP response\&. E\&.g\&.,
the webserver of back end \f(CWone\fP might insert a cookie named
\f(CWBalancerID\fP, having value \f(CWfirst\fP\&.
Second, Crossroads can insert such cookies using a carefully
-crafted directive \f(CWaddclientheader\fP\&. See below\&.
-.IP
-.IP "Header modification:"
+crafted directive \f(CWaddclientheader\fP\&.
+.IP "Syntax:"
+\f(CWstickycookie\fP \fIcookievalue\fP
+.IP "Default:"
+There is no default\&.
+
+.PP
+
+.SH "4\&.3\&.8: HTTP Header Modification Directives"
+.IP "Description:"
Crossroads understands the following
header modification directives: \f(CWaddclientheader\fP,
\f(CWappendclientheader\fP, \f(CWsetclientheader\fP, \f(CWaddserverheader\fP,
@@ -997,28 +1178,47 @@ and are being forwarded to back ends\&. When the destination is
\f(CWclient\fP, then Crossroads will apply such directives to
backend responses that are shuttled to the browser\&.
.IP
-The syntax of the directives is e\&.g\&. \f(CWaddclientheader
-"X-Processed-By: Crossroads";\fP\&. The directives expect one
+The format of the directives is e\&.g\&. \f(CWaddclientheader
+"X-Processed-By: Crossroads"\fP\&. The directives expect one
argument; a string, consisting of a header name, a colon, and a
-header value\&. The directive ends with a semicolon\&.
+header value\&. As usual, the directive must end with a semicolon\&.
.IP
The header value may contain one of the following formatting
directives:
.IP
.IP o
-\f(CW%r\fP is expanded to the real IP address of a client;
+\f(CW%a\fP is the availability of the current back end, when
+a current back end is established;
.IP o
-\f(CW%t\fP is expanded to a timestamp of the local time;
+\f(CW%1a\fP is the availability of the first back end (0 when
+unavailable, 1 if available); \f(CW%2a\fP is the availability of
+the second back end, and so on;
.IP o
-\f(CW%T\fP is expanded to a timestamp of Greenwich Mean Time;
+\f(CW%b\fP is the name of the current back end, when one is
+established;
.IP o
-\f(CW%v\fP is expanded to the Crossroads version;
+\f(CW%1b\fP is the name of the first back end, \f(CW%2b\fP of the
+second back end, and so on;
.IP o
-\f(CW%\fP\fIx\fP (where \fIx\fP is any other character) is
-expanded to \fIx\fP\&. E\&.g\&., \f(CW%%\fP is a literal % sign\&.
-.IP
-.IP "Common Uses"
-
+\f(CW%e\fP is the count of seconds since start of epoch
+(January 1st 1970 GMT);
+.IP o
+\f(CW%r\fP is the IP address of the client that requests a
+connection and for whom the external dispatcher should compute
+a back end;
+.IP o
+\f(CW%s\fP is the name of the current service that the client
+connected to;
+.IP o
+\f(CW%t\fP is the current local time in ASCII format, in
+\fIYYYY-MM-DD/hhh:mm:ss\fP;
+.IP o
+\f(CW%T\fP is the current GMT time in ASCIII format;
+.IP o
+\f(CW%v\fP is the Crossroads version;
+.IP o
+Any other chararacter following a \f(CW%\fP sign is taken
+literally; e\&.g\&. \f(CW%z\fP is just a z\&.
.IP
The following examples show common uses of header modifications\&.
.IP
@@ -1103,8 +1303,37 @@ service \&.\&.\&. {
}
}
.fi
+.IP "Syntax:"
+.IP o
+\f(CWaddclientheader\fP \fIHeadername: headervalue\fP to add a
+header in the traffic towards the client, even when another
+header \fIHeadername\fP exists;
+.IP o
+\f(CWappendclientheader\fP \fIHeadername: headervalue\fP to
+append \fIheadervalue\fP to an existing header \fIHeadername\fP
+in the traffic towards the client,
+or to add the whole header alltogether;
+.IP o
+\f(CWsetclientheader\fP \fIHeadername: headervalue\fP to
+overwrite an existing header in the traffic towards the
+client, or to add such a header;
+.IP o
+\f(CWaddserverheader\fP \fIHeadername: headervalue\fP to add a
+header in the traffic towards the server, even when another
+header \fIHeadername\fP exists;
+.IP o
+\f(CWappendserverheader\fP \fIHeadername: headervalue\fP to
+append \fIheadervalue\fP to an existing header \fIHeadername\fP
+in the traffic towards the server,
+or to add the whole header alltogether;
+.IP o
+\f(CWsetserverheader\fP \fIHeadername: headervalue\fP to
+overwrite an existing header in the traffic towards the
+server, or to add such a header\&.
+.IP "Default:"
+There is no default\&.
-.IP
+.PP
.SH "5: Tips, Tricks and Remarks"
@@ -1340,7 +1569,513 @@ hit\&. A \f(CWmaxconnections\fP statement on the level of that back may then
protect it\&.
.PP
-.SH "5\&.2: HTTP Session Stickiness"
+.SH "5\&.2: Using an external program to dispatch"
+
+.PP
+As mentioned before, Crossroads supports several built-in dispatch
+modes\&. However, you are always free to hook-in your own dispatch mode
+that determines the next back end using your own specific
+algorithm\&. This section explains how to do it\&.
+.PP
+
+.SH "5\&.2\&.1: Configuring the external handler"
+
+.PP
+First, the \f(CWdispatchmode\fP statement needs to inform Crossroads that
+an external program will do the job\&. The syntax is: \f(CWdispatchmode
+externalhandler\fP \fIprogram arguments\fP\&. The \fIprogram\fP must point to
+an executable program that will be started by Crossroads\&. The
+specifier \fIarguments\fP can be anything you want; those will be the
+arguments to Crossroads\&. You can however use the following special
+format specifiers:
+.PP
+.IP o
+\f(CW%a\fP is the availability of the current back end, when
+a current back end is established;
+.IP o
+\f(CW%1a\fP is the availability of the first back end (0 when
+unavailable, 1 if available); \f(CW%2a\fP is the availability of
+the second back end, and so on;
+.IP o
+\f(CW%b\fP is the name of the current back end, when one is
+established;
+.IP o
+\f(CW%1b\fP is the name of the first back end, \f(CW%2b\fP of the
+second back end, and so on;
+.IP o
+\f(CW%e\fP is the count of seconds since start of epoch
+(January 1st 1970 GMT);
+.IP o
+\f(CW%r\fP is the IP address of the client that requests a
+connection and for whom the external dispatcher should compute
+a back end;
+.IP o
+\f(CW%s\fP is the name of the current service that the client
+connected to;
+.IP o
+\f(CW%t\fP is the current local time in ASCII format, in
+\fIYYYY-MM-DD/hhh:mm:ss\fP;
+.IP o
+\f(CW%T\fP is the current GMT time in ASCIII format;
+.IP o
+\f(CW%v\fP is the Crossroads version;
+.IP o
+Any other chararacter following a \f(CW%\fP sign is taken
+literally; e\&.g\&. \f(CW%z\fP is just a z\&.
+.PP
+Note that the format specifiers such as \f(CW%b\fP don\&'t make sense in the
+phase in which an external handler is called, since there is no
+current back end yet (the job of the handler is to supply one)\&.
+.PP
+
+.SH "5\&.2\&.2: Writing the external handler"
+
+.PP
+The external handler is activated using the arguments that are
+specified in \f(CW/etc/crossroads\&.conf\fP\&. The external handler can do
+whatever it wants, but ultimately, it must write a back end name on
+its \fIstdout\fP\&. Crossroads reads this, and if the back end is
+available, uses that back end for the connection\&.
+.PP
+
+.SH "5\&.2\&.3: Examples of external handlers"
+
+.PP
+This section shows some examples of Crossroads configurations
+vs\&. external handlers\&. The sample handlers that are shown here, are
+also included in the Crossroads distribution, under the directory
+\f(CWetc/\fP\&. Also note that the examples shown here are just
+quick-and-dirty Perl scripts, meant to illustrate only\&. Your
+applications may need other external handlers, but you can use the
+shown scripts as a starting point\&.
+.PP
+.SH "Round-robin dispatching"
+
+.PP
+This example is trivial in the sense that round-robin dispatching is
+already built into Crossroads, so
+that using an external handler for this purpose only slows down
+Crossroads\&. However, it\&'s a good starting example\&.
+.PP
+The Crossroads configuration is shown below:
+.PP
+.nf
+service test {
+ port 8001;
+ verbosity on;
+ revivinginterval 5;
+
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/dispatcher-roundrobin
+ %1b %1a %2b %2a;
+
+ backend testone {
+ server localhost:3128;
+ verbosity on;
+ }
+ backend testtwo {
+ server locallhost:3128;
+ verbosity on;
+ }
+}
+.fi
+
+.PP
+The relevant \f(CWdispatchmode\fP statement invokes the external program
+\f(CWdispatcher-roundrobin\fP with four arguments: the name of the first
+back end (\f(CWtestone\fP), its availability (0 or 1), the name of the
+second back end (\f(CWtesttwo\fP) and its availability (0 or 1)\&.
+.PP
+The external handler, which is also included in the Crossroads
+distribution, is shown below\&. It is a Perl script\&.
+.PP
+.nf
+#!/usr/bin/perl
+
+use strict;
+
+# Example of a round-robin external dispatcher\&. This is totally
+# superfluous, Crossroads has this on-board; if you use the external
+# program for determining round-robin dispatching, then you\&'ll only
+# slow things down\&. This script is just meant as an example\&.
+
+# Globals / configuration
+# -----------------------
+my $log = \&'/tmp/exthandler\&.log\&'; # Debug log, set to /dev/null to suppress
+my $statefile = \&'/tmp/rr\&.last\&'; # Where we keep the last used
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq \&'/dev/null\&' or $log eq \&'\&');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), \&' \&', @_);
+}
+
+# Read the last used back end
+# ---------------------------
+sub readlast() {
+ my $ret;
+
+ if (open (my $if, $statefile)) {
+ $ret = <$if>;
+ chomp ($ret);
+ close ($if);
+ msg ("Last used back end: $ret\en");
+ return ($ret);
+ }
+ msg ("No last-used back end (yet)\en");
+ return (undef);
+}
+
+# Write back the last used back end, reply to Crossroads and stop
+# ---------------------------------------------------------------
+sub reply ($) {
+ my $last = shift;
+
+ if (open (my $of, ">$statefile")) {
+ print $of ("$last\en");
+ }
+ print ("$last\en");
+ exit (0);
+}
+
+# Main starts here
+# ----------------
+
+# Collect the cmdline arguments\&. We expect pairs of backend-name /
+# backend-availablility, and we\&'ll store only the available ones\&.
+msg ("Dispatch request received\en");
+my @backend;
+for (my $i = 0; $i <= $#ARGV; $i += 2) {
+ push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]);
+}
+msg ("Available back ends: @backend\en");
+
+# Let\&'s see what the last one is\&. If none found, then we return the
+# first available back end\&. Otherwise we need to go thru the list of
+# back ends, and return the next one in line\&.
+my $last = readlast();
+if ($last eq \&'\&') {
+ msg ("Returning first available back end $backend[0]\en");
+ reply ($backend[0]);
+}
+
+# There **was** a last back end\&. Try to match it in the list,
+# then return the next-in-line\&.
+for (my $i = 0; $i < $#backend; $i++) {
+ if ($last eq $backend[$i]) {
+ msg ("Returning next back end ", $backend[$i + 1], "\en");
+ reply ($backend[$i + 1]);
+ }
+}
+
+# No luck\&.\&. run back to the first one\&.
+msg ("Returning first back end $backend[0]\en");
+reply ($backend[0]);
+.fi
+
+.PP
+The working of the script is basically as follows:
+.PP
+.IP o
+The argument list is scanned\&. Back ends that are
+available are collected in an array \f(CW@backend\fP\&.
+.IP
+.IP o
+The script queries a state file \f(CW/tmp/rr\&.last\fP\&. If a
+back end name occurs there, then the next back end is looked
+up in \f(CW@backend\fP and returned to Crossroads\&. If no last back
+is unknown or can\&'t be matched, then the first available back
+end (first element of \f(CW@backend\fP) is returned to Crossroads\&.
+.IP
+.IP o
+Informing Crossroads is done via the subroutine
+\f(CWreply()\fP\&. This code writes the selected back end to file
+\f(CW/tmp/rr\&.last\fP (for future usage) and prints the back end
+name to \fIstdout\fP\&.
+.IP
+.IP o
+The script logs its actions to a file
+\f(CW/tmp/exthandler\&.log\fP\&. This log file can be inspected for
+the script\&'s actions\&.
+.PP
+.SH "Dispatching by the client IP address"
+
+.PP
+The following example shows a useful real-life situation\&. The
+situation is as follows:
+.PP
+.IP o
+Crossroads is used as a single-address point to forward
+Remote Desktop requests to a farm of Windows systems, where
+users can work via remote access;
+.IP
+.IP o
+However, users may stop their session, and when they
+re-connect, they expect to be sent to the Windows system that
+they had worked on previously;
+.IP
+.IP o
+Client PC\&'s have their distinct IP addresses, which
+distinguishes them\&.
+.IP
+.IP o
+Of four windows systems, two are large servers, and two
+are small ones\&. We\&'ll want to assign large servers to clients
+when we have a choice\&.
+.PP
+The requirements resemble session stickiness in HTTP, except that the remote
+desktop protocol doesn\&'t support stickiness\&. This situation is a
+perfect example of how an external handler can help:
+.PP
+.IP o
+A suitable dispatch mode isn\&'t yet available in
+Crossroads, but can be easily coded in an external handler;
+.IP
+.IP o
+The potential delay due to the calling of an external
+handler won\&'t even be noticed\&. This is a network service where
+the connection time isn\&'t critical; we\&'d expect only a few
+(albeit lengthy) TCP connections\&.
+.PP
+The approach to the solution of this problem uses several external
+program hooks:
+.PP
+.IP o
+An external dispatcher handler will be responsible for
+suggesting a back end, given a client IP and given the current
+timestamp\&. This handler will consult an internal
+administration to see whether the stated IP address should
+re-use a back end, or to determine which back end is free for usage\&.
+.IP o
+An external hook \f(CWonstart\fP will be responsible for
+updating the internal administration; i\&.e\&., to flag a back end
+as \&'occupied\&'\&.
+.IP o
+The external hooks \f(CWonfailure\fP and \f(CWonend\fP will be
+responsible for flagging a back end as \&'free\&' again; i\&.e\&., for
+erasing any previous information that states that the back end
+was occupied\&.
+.PP
+The Crossroads configuration is shown below\&. Only four Windows back
+ends are shown\&. Each back end is configured on a
+given IP address, port 3389, and is limited to one concurrent connection
+(otherwise a new user might \&'steal\&' a running desktop session)\&.
+.PP
+.nf
+service rdp {
+ port 3389;
+ revivinginterval 5;
+
+ /* rdp-helper dispatch IP STAMP \&.\&.\&. will suggest a back end to use,
+ * arguments are for all back ends: name, availability, weight */
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e
+ %1b %1a %1w
+ %2b %2a %2w
+ %3b %3a %3w
+ %4b %4a %4w;
+
+ backend win1 {
+ server 10\&.1\&.1\&.1:3389;
+ maxconnections 1;
+ /* rdp-helper start IP STAMP BACKEND will log the actual start
+ * of a connection;
+ * rdp-helper end IP will log the ending of a connection */
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win2 {
+ server 10\&.1\&.1\&.2:3389;
+ maxconnections 1;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win3 {
+ server 10\&.1\&.1\&.3:3389;
+ maxconnections 1;
+ weight 2;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win4 {
+ server 10\&.1\&.1\&.4:3389;
+ maxconnections 1;
+ weight 3;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+}
+.fi
+
+.PP
+Depending on the dispatcher stage, the exernal handler \f(CWrdp-helper\fP
+is invoked in different ways:
+.PP
+.IP "During dispatching"
+the helper is called to suggest a back
+end\&. The arguments are an action indicator \f(CWdispatch\fP, the
+client\&'s IP address, the timestamp, and four triplets that
+represent back ends: per back end its name, its availability,
+and its weight\&. The purpose of the helper is to tell
+Crossroads which back end to use\&.
+.IP
+.IP "During connection start"
+the helper will be invoked to
+inform it of the start of a connection, given a client IP
+address\&.
+.IP
+.IP "When a connection terminates"
+the helper will be invoked
+to inform it that the connection has ended\&.
+.PP
+Here\&'s the external handler as Perl script\&. It uses the module
+\f(CWGDBM_File\fP which most likely will not be part of standard Perl
+distributions, but can be added using CPAN\&. (Alternatively, any other
+database module can be used\&.)
+.PP
+.nf
+#!/usr/bin/perl
+
+use strict;
+use GDBM_File;
+
+# Global variables and configuration
+# ----------------------------------
+my $log = \&'/tmp/exthandler\&.log\&'; # Debug log, set to /dev/null to suppress
+my $cdb = \&'/tmp/client\&.db\&'; # GDBM database of clients
+my %db; # \&.\&. and memory representation of it
+my $timeout = 24*60*60; # Timeout of a connection in secs
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq \&'/dev/null\&' or $log eq \&'\&');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), \&' \&', @_);
+ close ($of);
+}
+
+# Reply a back end to the caller and stop processing\&.
+# ---------------------------------------------------
+sub reply ($) {
+ my $b = shift;
+ msg ("Suggesting $b to Crossroads\&.\en");
+ print ("$b\en");
+ exit (0);
+}
+
+# Is a value in an array
+# ----------------------
+sub inarray {
+ my $val = shift;
+ for my $other (@_) {
+ return (1) if ($other eq $val);
+ }
+ return (0);
+}
+
+# A connection is starting
+# ------------------------
+sub start {
+ my ($ip, $stamp, $backend) = @_;
+ msg ("Logging START of connection for IP $ip on stamp $stamp, ",
+ "back end $backend\en");
+ $db{$ip} = "$backend:$stamp";
+}
+
+# A connection has ended
+# ----------------------
+sub end {
+ my $ip = shift;
+ msg ("Logging END of connection for IP $ip\en");
+ $db{$ip} = undef;
+}
+
+# Request to determine a back end
+# -------------------------------
+sub dispatch {
+ my $ip = shift;
+ my $stamp = shift;
+
+ msg ("Request to dispatch IP $ip on stamp $stamp\en");
+
+ # Read the next arguments\&. They are triplets of
+ # backend-name / availability / weight\&. Store if the back end is
+ # available\&.
+ my (@backends, @weights);
+ for (my $i = 0; $i < $#_; $i += 3) {
+ if ($_[$i + 1] != 0) {
+ push (@backends, $_[$i]);
+ push (@weights, $_[$i + 2]);
+ msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\en");
+ }
+ }
+
+ # See if this is a reconnect by a previously seen client IP\&. We\&'ll
+ # treat this as a reconnect if the timeout wasn\&'t yet exceeded\&.
+ if ($db{$ip} ne \&'\&') {
+ my ($last_backend, $last_stamp) = split (/:/, $db{$ip});
+ msg ("IP $ip had last connected on $last_stamp to $last_backend\en");
+ if ($stamp < $last_stamp + $timeout) {
+ msg ("Timeout not yet exceeded, this may be a reconnect\en");
+ # We\&'ll allow a reconnect only if the stated last_backend is
+ # free (sanity check)\&.
+ if (inarray ($last_backend, @backends)) {
+ msg ("Last back end $last_backend is available, ",
+ "letting through\en");
+ reply ($last_backend);
+ } else {
+ msg ("Last used back end isn\&'t free, suggesting a new one\en");
+ }
+ } else {
+ msg ("Timeout exceeded, suggesting a new back end\en");
+ }
+ } else {
+ msg ("Np preveious connection data, suggesting a new back end\en");
+ }
+
+ my $bestweight = -1;
+ my $bestbackend;
+ for (my $i = 0; $i <= $#weights; $i++) {
+ if ($bestweight == -1 or $bestweight > $weights[$i]) {
+ $bestweight = $weights[$i];
+ $bestbackend = $backends[$i];
+ }
+ }
+
+ msg ("Best back end: $bestbackend (given weight $bestweight)\en");
+ reply ($bestbackend);
+}
+
+# Main starts here
+# ----------------
+msg ("Start of run, attaching GDBM database \&'$cdb\&'\en");
+tie (%db, \&'GDBM_File\&', $cdb, &GDBM_WRCREAT, 0600);
+
+# The first argument must be an action \&'dispatch\&', \&'start\&' or \&'end\&'\&.
+# Depending on the action, we do stuff\&.
+my $action = shift (@ARGV);
+if ($action eq \&'dispatch\&') {
+ dispatch (@ARGV);
+} elsif ($action eq \&'start\&') {
+ start (@ARGV);
+} elsif ($action eq \&'end\&') {
+ end (@ARGV);
+} else {
+ print STDERR ("Usage: rdp-helper {dispatch|start|end} args\en");
+ exit (1);
+}
+.fi
+
+.PP
+
+.SH "5\&.3: HTTP Session Stickiness"
.PP
This section focuses on HTTP session stickiness\&. This term refers to
@@ -1350,7 +2085,7 @@ back end is selected by the balancer, it will remain the back end of
choice, even for subsequent connections\&.
.PP
-.SH "5\&.2\&.1: Don\&'t use stickiness!"
+.SH "5\&.3\&.1: Don\&'t use stickiness!"
.PP
The rule of thumb as far as the balancer is concerned, is: \fBDo not
@@ -1385,7 +2120,7 @@ servers such as Websphere can be configured to replicate session data
between nodes\&.
.PP
-.SH "5\&.2\&.2: But if you must\&.\&."
+.SH "5\&.3\&.2: But if you must\&.\&."
.PP
However, if you \fBmust\fP use session stickiness, then proceed as
@@ -1419,7 +2154,7 @@ Below is a short example of a configuration\&.
.nf
service www {
port 80;
- type stickyhttp;
+ type http;
revivinginterval 15;
dispatchmode byconnections;
@@ -1443,7 +2178,7 @@ Note how the cookie names and values in the directives
prerequisite for stickiness\&.
.PP
-.SH "5\&.3: Passing the client\&'s IP address"
+.SH "5\&.4: Passing the client\&'s IP address"
.PP
Since Crossroads just shuttles bytes to and fro, meta-information of
@@ -1475,7 +2210,7 @@ performance will be hampered -- all passing messages will have to be
unpacked and analyzed\&.
.PP
-.SH "5\&.3\&.1: Sample Crossroads configuration"
+.SH "5\&.4\&.1: Sample Crossroads configuration"
.PP
The below sample configuration shows two HTTP back ends that receive
@@ -1503,7 +2238,7 @@ service www {
.PP
-.SH "5\&.3\&.2: Sample Apache configuration"
+.SH "5\&.4\&.2: Sample Apache configuration"
.PP
The method by which each back end analyzes the header \f(CWX-Real-IP\fP
@@ -1536,7 +2271,7 @@ LogFormat "%{X-Real-IP}i %l %u %t %D \e"%r\e" %>s %b" common
.PP
-.SH "5\&.4: Debugging network traffic"
+.SH "5\&.5: Debugging network traffic"
.PP
Incase the traffic between
@@ -1668,7 +2403,153 @@ analyze the output and to compute round trip times\&. Such scripts
are not (yet) included in Crossroads\&.
.PP
-.SH "5\&.5: Configuration examples"
+.SH "5\&.6: Limiting Access to Crossroads by Client IP Address"
+
+.PP
+
+.SH "5\&.6\&.1: General Examples"
+
+.PP
+The directives \f(CWallowfrom\fP, \f(CWdenyfrom\fP, \f(CWallowfile\fP and
+\f(CWdenyfile\fP can be used to instruct Crossroads to specifically allow
+access by using a "whitelist" of IP addresses, or to specifically deny
+access by using a "blacklist"\&. E\&.g\&., the following configuration
+allows access to service \f(CWwebproxy\fP only to \fIlocalhost\fP:
+.PP
+.nf
+service webproxy {
+ port 8000;
+ allowfrom 127\&.0\&.0\&.1;
+ backend one {
+ \&.
+ \&. Back end definitions occur here
+ \&.
+ }
+ \&.
+ \&. Other back ends or other service directives
+ \&. may occur here
+ \&.
+}
+.fi
+
+.PP
+In this example there is a "whitelist" having only one entry: IP
+address 127\&.0\&.0\&.1, or \fIlocalhost\fP\&. (Incidentally, the same behaviour
+could be accomplished by stating \fIbindto 127\&.0\&.0\&.1\fP, in which case
+Crossroads would only listen to the local network device\&.)
+.PP
+In the same vein, the directive \f(CWallowfrom 127\&.0\&.0\&.1 192\&.168\&.1/24\fP
+would allow access to \fIlocalhost\fP and to all IP addresses that start
+with 192\&.168\&.1\&. The specifier \f(CW192\&.168\&.1/24\fP states that there are
+three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are
+relevant; so that the fourth network byte doesn\&'t matter\&.
+.PP
+
+.SH "5\&.6\&.2: Using External Files"
+
+.PP
+The directives \f(CWallowfile\fP and \f(CWdenyfile\fP allow you to specify IP
+addresses in external files\&. The Crossroads configuration states
+e\&.g\&. \f(CWallowfile /tmp/allow\&.txt\fP, and the IP addresses are then in
+\f(CW/tmp/allow\&.txt\fP\&. The format of \f(CW/tmp/allow\&.txt\fP is as follows:
+.PP
+.IP o
+The specifications follow again \fIp\&.q\&.r\&.s/mask\fP, where
+p, q, r and s are network bytes which can be left out on the
+right hand side when the mask allows it;
+.IP
+.IP o
+The specifications must be separated by white space
+(spaces, tabs or newlines)\&.
+.PP
+E\&.g\&., the following is a valid example of an external specification
+file:
+.PP
+.nf
+127\&.0\&.0\&.1
+192\&.168\&.1/24
+10/8
+.fi
+
+.PP
+When external files are in effect, then the signal \f(CWSIGHUP\fP (1)
+causes Crossroads to reload the external file\&. E\&.g\&., while Crossroads
+is running, you may edit \f(CW/tmp/allow\&.txt\fP, and then issue \f(CWkillall
+-1 crossroads\fP\&. The new contents of \f(CW/tmp/allow\&.txt\fP will be
+reloaded\&.
+.PP
+
+.SH "5\&.6\&.3: Mixing Directives"
+
+.PP
+Crossroads allows to mix all directives in one service
+description\&. However, some mixes are less meaningful than others\&. It\&'s
+up to you to take this into account\&.
+.PP
+The following rules apply:
+.PP
+.IP o
+Blacklisting and whitelisting can be used together\&. When
+combined, the blacklist will always be interpreted
+first\&. E\&.g\&., consider the following directives:
+.IP
+.nf
+allowfrom 192\&.168\&.1/24
+denyfrom 192\&.168\&.1\&.100
+.fi
+
+.IP
+Given the fact that the deny list is checked first, client
+192\&.168\&.1\&.100 won\&'t be able to access Crossroads\&. Then the
+allow list will be checked, stating that all clients whose IP
+address starts with 192\&.168\&.1 may connect\&. The effect will be
+that e\&.g\&., client 192\&.168\&.1\&.1 may connect, 192\&.168\&.1\&.2 may
+connect too, 192\&.168\&.1\&.100 will be blocked, and 10\&.1\&.1\&.1 will
+be blocked as well\&.
+.IP
+Now consider the following directives:
+.IP
+.nf
+allowfrom 192\&.168\&.1\&.100 127\&.0\&.0\&.1
+denyfrom 192\&.168\&.1/24
+.fi
+
+.IP
+This will first of all deny access to all IP addresses that
+start with 192\&.168\&.1\&. So the rule that allows 192\&.168\&.1\&.100
+won\&'t ever be effective\&. The net result will be that access
+will be granted to 127\&.0\&.0\&.1, and IP addresses that don\&'t
+match 192\&.168\&.1/24\&.
+.IP
+.IP o
+Blacklisting or whitelisting can be left out\&.
+A list is considered empty when no appropriate directives
+occur in \f(CW/etc/crossroads\&.conf\fP, or when the directive
+points to an empty or non-existent external file\&.
+.IP
+.IP o
+Using \f(CW*from\fP and \f(CW*file\fP statements is allowed, but
+doesn\&'t make sense\&. E\&.g\&., the following configuration sample
+is such a case:
+.IP
+.nf
+allowfrom 127\&.0\&.0\&.1 192\&.168\&.1/24
+allowfile /tmp/allow\&.txt
+.fi
+
+.IP
+There is a technical reason for this\&. Once Crossroads
+processes the \f(CWallowfile\fP directive, then the whole
+whitelist is cleared (thereby removing the entries 127\&.0\&.0\&.1
+and 192\&.168\&.1/24), and new entries are reloaded from the
+file\&. The net result is that the \f(CWallowfrom\fP specification
+is overruled\&.
+.IP
+Crossroads doesn\&'t check for such configurations, which are
+syntactially correct, but make no semantic sense\&.
+.PP
+
+.SH "5\&.7: Configuration examples"
.PP
As a general hint, use \f(CWcrossroads sampleconf\fP to view the most
@@ -1676,7 +2557,7 @@ up-to-date examples of configurations\&. The description below shows a
few examples too\&.
.PP
-.SH "5\&.5\&.1: A load balancer for three webserver back ends"
+.SH "5\&.7\&.1: A load balancer for three webserver back ends"
.PP
The following configuration example binds crossroads to port 80 of the
@@ -1806,7 +2687,7 @@ service www {
.PP
-.SH "5\&.5\&.2: An HTTP forwarder when travelling"
+.SH "5\&.7\&.2: An HTTP forwarder when travelling"
.PP
As another example, here\&'s my \f(CWcrossroads\&.conf\fP that I use on my
@@ -1901,7 +2782,7 @@ sshtunnel down\fP will \&'take down\&' the back end \f(CWSshTunnel\fP -- and
will automatically cause crossroads to switch to \f(CWLocalSquid\fP\&.
.PP
-.SH "5\&.5\&.3: SSH login with enforced idle logout"
+.SH "5\&.7\&.3: SSH login with enforced idle logout"
.PP
The following example shows how crossroads \&'throttles\&' SSH
diff --git a/doc/crossroads.pdf b/doc/crossroads.pdf
Binary files differ.
diff --git a/doc/formattable.yo b/doc/formattable.yo
@@ -0,0 +1,23 @@
+itemization(
+ it() tt(%a) is the availability of the current back end, when
+ a current back end is established;
+ it() tt(%1a) is the availability of the first back end (0 when
+ unavailable, 1 if available); tt(%2a) is the availability of
+ the second back end, and so on;
+ it() tt(%b) is the name of the current back end, when one is
+ established;
+ it() tt(%1b) is the name of the first back end, tt(%2b) of the
+ second back end, and so on;
+ it() tt(%e) is the count of seconds since start of epoch
+ (January 1st 1970 GMT);
+ it() tt(%r) is the IP address of the client that requests a
+ connection and for whom the external dispatcher should compute
+ a back end;
+ it() tt(%s) is the name of the current service that the client
+ connected to;
+ it() tt(%t) is the current local time in ASCII format, in
+ em(YYYY-MM-DD/hhh:mm:ss);
+ it() tt(%T) is the current GMT time in ASCIII format;
+ it() tt(%v) is the Crossroads version;
+ it() Any other chararacter following a tt(%) sign is taken
+ literally; e.g. tt(%z) is just a z.)
diff --git a/doc/intro.yo b/doc/intro.yo
@@ -154,6 +154,20 @@ description(
controlled manner, without any client noticing it.
)
+subsect(Porting issues for pre-1.21 installations)
+
+ As of version 1.21, the event-hook directives tt(onsuccess) and
+ tt(onfailure) no longer exists.
+
+ itemization(
+ it() Please replace tt(onsuccess) by tt(onstart);
+ it() Please replace tt(onfailure) bu tt(onfail);
+ it() Note that there is a new hook tt(onend).)
+
+ The commands that are run via tt(onstart), tt(onend) or tt(onfail)
+ are subject to format expansion; e.g., tt(%1w) is expanded to the
+ weight of the first back end, etc.. See section ref(config) for details.
+
subsect(Porting issues for pre-0.26 installations)
As of version 0.26 the syntax of the configuration file has
diff --git a/doc/tips.yo b/doc/tips.yo
@@ -215,6 +215,454 @@ hit. A tt(maxconnections) statement on the level of that back may then
protect it.
+subsect(Using an external program to dispatch)
+label(externalhandler)
+
+As mentioned before, Crossroads supports several built-in dispatch
+modes. However, you are always free to hook-in your own dispatch mode
+that determines the next back end using your own specific
+algorithm. This section explains how to do it.
+
+subsubsect(Configuring the external handler)
+
+First, the tt(dispatchmode) statement needs to inform Crossroads that
+an external program will do the job. The syntax is: tt(dispatchmode
+externalhandler) em(program arguments). The em(program) must point to
+an executable program that will be started by Crossroads. The
+specifier em(arguments) can be anything you want; those will be the
+arguments to Crossroads. You can however use the following special
+format specifiers:
+
+INCLUDEFILE(formattable)
+
+Note that the format specifiers such as tt(%b) don't make sense in the
+phase in which an external handler is called, since there is no
+current back end yet (the job of the handler is to supply one).
+
+subsubsect(Writing the external handler)
+
+The external handler is activated using the arguments that are
+specified in tt(/etc/crossroads.conf). The external handler can do
+whatever it wants, but ultimately, it must write a back end name on
+its em(stdout). Crossroads reads this, and if the back end is
+available, uses that back end for the connection.
+
+subsubsect(Examples of external handlers)
+
+This section shows some examples of Crossroads configurations
+vs. external handlers. The sample handlers that are shown here, are
+also included in the Crossroads distribution, under the directory
+tt(etc/). Also note that the examples shown here are just
+quick-and-dirty Perl scripts, meant to illustrate only. Your
+applications may need other external handlers, but you can use the
+shown scripts as a starting point.
+
+subsubsubsect(Round-robin dispatching)
+
+This example is trivial in the sense that round-robin dispatching is
+already built into Crossroads, so
+that using an external handler for this purpose only slows down
+Crossroads. However, it's a good starting example.
+
+The Crossroads configuration is shown below:
+
+verb(\
+service test {
+ port 8001;
+ verbosity on;
+ revivinginterval 5;
+
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/dispatcher-roundrobin
+ %1b %1a %2b %2a;
+
+ backend testone {
+ server localhost:3128;
+ verbosity on;
+ }
+ backend testtwo {
+ server locallhost:3128;
+ verbosity on;
+ }
+})
+
+The relevant tt(dispatchmode) statement invokes the external program
+tt(dispatcher-roundrobin) with four arguments: the name of the first
+back end (tt(testone)), its availability (0 or 1), the name of the
+second back end (tt(testtwo)) and its availability (0 or 1).
+
+The external handler, which is also included in the Crossroads
+distribution, is shown below. It is a Perl script.
+
+verb(\
+#!/usr/bin/perl
+
+use strict;
+
+# Example of a round-robin external dispatcher. This is totally
+# superfluous, Crossroads has this on-board; if you use the external
+# program for determining round-robin dispatching, then you'll only
+# slow things down. This script is just meant as an example.
+
+# Globals / configuration
+# -----------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $statefile = '/tmp/rr.last'; # Where we keep the last used
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+}
+
+# Read the last used back end
+# ---------------------------
+sub readlast() {
+ my $ret;
+
+ if (open (my $if, $statefile)) {
+ $ret = <$if>;
+ chomp ($ret);
+ close ($if);
+ msg ("Last used back end: $ret\n");
+ return ($ret);
+ }
+ msg ("No last-used back end (yet)\n");
+ return (undef);
+}
+
+# Write back the last used back end, reply to Crossroads and stop
+# ---------------------------------------------------------------
+sub reply ($) {
+ my $last = shift;
+
+ if (open (my $of, ">$statefile")) {
+ print $of ("$last\n");
+ }
+ print ("$last\n");
+ exit (0);
+}
+
+# Main starts here
+# ----------------
+
+# Collect the cmdline arguments. We expect pairs of backend-name /
+# backend-availablility, and we'll store only the available ones.
+msg ("Dispatch request received\n");
+my @backend;
+for (my $i = 0; $i <= $#ARGV; $i += 2) {
+ push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]);
+}
+msg ("Available back ends: @backend\n");
+
+# Let's see what the last one is. If none found, then we return the
+# first available back end. Otherwise we need to go thru the list of
+# back ends, and return the next one in line.
+my $last = readlast();
+if ($last eq '') {
+ msg ("Returning first available back end $backend[0]\n");
+ reply ($backend[0]);
+}
+
+# There **was** a last back end. Try to match it in the list,
+# then return the next-in-line.
+for (my $i = 0; $i < $#backend; $i++) {
+ if ($last eq $backend[$i]) {
+ msg ("Returning next back end ", $backend[$i + 1], "\n");
+ reply ($backend[$i + 1]);
+ }
+}
+
+# No luck.. run back to the first one.
+msg ("Returning first back end $backend[0]\n");
+reply ($backend[0]);)
+
+The working of the script is basically as follows:
+
+itemization(
+ it() The argument list is scanned. Back ends that are
+ available are collected in an array tt(@backend).
+
+ it() The script queries a state file tt(/tmp/rr.last). If a
+ back end name occurs there, then the next back end is looked
+ up in tt(@backend) and returned to Crossroads. If no last back
+ is unknown or can't be matched, then the first available back
+ end (first element of tt(@backend)) is returned to Crossroads.
+
+ it() Informing Crossroads is done via the subroutine
+ tt(reply()). This code writes the selected back end to file
+ tt(/tmp/rr.last) (for future usage) and prints the back end
+ name to em(stdout).
+
+ it() The script logs its actions to a file
+ tt(/tmp/exthandler.log). This log file can be inspected for
+ the script's actions.)
+
+
+subsubsubsect(Dispatching by the client IP address)
+
+The following example shows a useful real-life situation. The
+situation is as follows:
+
+itemization(
+ it() Crossroads is used as a single-address point to forward
+ Remote Desktop requests to a farm of Windows systems, where
+ users can work via remote access;
+
+ it() However, users may stop their session, and when they
+ re-connect, they expect to be sent to the Windows system that
+ they had worked on previously;
+
+ it() Client PC's have their distinct IP addresses, which
+ distinguishes them.
+
+ it() Of four windows systems, two are large servers, and two
+ are small ones. We'll want to assign large servers to clients
+ when we have a choice.)
+
+The requirements resemble session stickiness in HTTP, except that the remote
+desktop protocol doesn't support stickiness. This situation is a
+perfect example of how an external handler can help:
+
+itemization(
+ it() A suitable dispatch mode isn't yet available in
+ Crossroads, but can be easily coded in an external handler;
+
+ it() The potential delay due to the calling of an external
+ handler won't even be noticed. This is a network service where
+ the connection time isn't critical; we'd expect only a few
+ (albeit lengthy) TCP connections.)
+
+The approach to the solution of this problem uses several external
+program hooks:
+
+itemization(
+ it() An external dispatcher handler will be responsible for
+ suggesting a back end, given a client IP and given the current
+ timestamp. This handler will consult an internal
+ administration to see whether the stated IP address should
+ re-use a back end, or to determine which back end is free for usage.
+ it() An external hook tt(onstart) will be responsible for
+ updating the internal administration; i.e., to flag a back end
+ as 'occupied'.
+ it() The external hooks tt(onfailure) and tt(onend) will be
+ responsible for flagging a back end as 'free' again; i.e., for
+ erasing any previous information that states that the back end
+ was occupied.)
+
+The Crossroads configuration is shown below. Only four Windows back
+ends are shown. Each back end is configured on a
+given IP address, port 3389, and is limited to one concurrent connection
+(otherwise a new user might 'steal' a running desktop session).
+
+verb(\
+service rdp {
+ port 3389;
+ revivinginterval 5;
+
+ /* rdp-helper dispatch IP STAMP ... will suggest a back end to use,
+ * arguments are for all back ends: name, availability, weight */
+ dispatchmode externalhandler
+ /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e
+ %1b %1a %1w
+ %2b %2a %2w
+ %3b %3a %3w
+ %4b %4a %4w;
+
+ backend win1 {
+ server 10.1.1.1:3389;
+ maxconnections 1;
+ /* rdp-helper start IP STAMP BACKEND will log the actual start
+ * of a connection;
+ * rdp-helper end IP will log the ending of a connection */
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win2 {
+ server 10.1.1.2:3389;
+ maxconnections 1;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win3 {
+ server 10.1.1.3:3389;
+ maxconnections 1;
+ weight 2;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+ backend win4 {
+ server 10.1.1.4:3389;
+ maxconnections 1;
+ weight 3;
+ onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
+ onend /usr/local/src/crossroads/etc/rdp-helper end %r;
+ onfail /usr/local/src/crossroads/etc/rdp-helper end %r;
+ }
+})
+
+Depending on the dispatcher stage, the exernal handler tt(rdp-helper)
+is invoked in different ways:
+
+description(
+ dit(During dispatching) the helper is called to suggest a back
+ end. The arguments are an action indicator tt(dispatch), the
+ client's IP address, the timestamp, and four triplets that
+ represent back ends: per back end its name, its availability,
+ and its weight. The purpose of the helper is to tell
+ Crossroads which back end to use.
+
+ dit(During connection start) the helper will be invoked to
+ inform it of the start of a connection, given a client IP
+ address.
+
+ dit(When a connection terminates) the helper will be invoked
+ to inform it that the connection has ended.)
+
+Here's the external handler as Perl script. It uses the module
+tt(GDBM_File) which most likely will not be part of standard Perl
+distributions, but can be added using CPAN. (Alternatively, any other
+database module can be used.)
+
+verb(\
+#!/usr/bin/perl
+
+use strict;
+use GDBM_File;
+
+# Global variables and configuration
+# ----------------------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $cdb = '/tmp/client.db'; # GDBM database of clients
+my %db; # .. and memory representation of it
+my $timeout = 24*60*60; # Timeout of a connection in secs
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+ close ($of);
+}
+
+# Reply a back end to the caller and stop processing.
+# ---------------------------------------------------
+sub reply ($) {
+ my $b = shift;
+ msg ("Suggesting $b to Crossroads.\n");
+ print ("$b\n");
+ exit (0);
+}
+
+# Is a value in an array
+# ----------------------
+sub inarray {
+ my $val = shift;
+ for my $other (@_) {
+ return (1) if ($other eq $val);
+ }
+ return (0);
+}
+
+# A connection is starting
+# ------------------------
+sub start {
+ my ($ip, $stamp, $backend) = @_;
+ msg ("Logging START of connection for IP $ip on stamp $stamp, ",
+ "back end $backend\n");
+ $db{$ip} = "$backend:$stamp";
+}
+
+# A connection has ended
+# ----------------------
+sub end {
+ my $ip = shift;
+ msg ("Logging END of connection for IP $ip\n");
+ $db{$ip} = undef;
+}
+
+# Request to determine a back end
+# -------------------------------
+sub dispatch {
+ my $ip = shift;
+ my $stamp = shift;
+
+ msg ("Request to dispatch IP $ip on stamp $stamp\n");
+
+ # Read the next arguments. They are triplets of
+ # backend-name / availability / weight. Store if the back end is
+ # available.
+ my (@backends, @weights);
+ for (my $i = 0; $i < $#_; $i += 3) {
+ if ($_[$i + 1] != 0) {
+ push (@backends, $_[$i]);
+ push (@weights, $_[$i + 2]);
+ msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n");
+ }
+ }
+
+ # See if this is a reconnect by a previously seen client IP. We'll
+ # treat this as a reconnect if the timeout wasn't yet exceeded.
+ if ($db{$ip} ne '') {
+ my ($last_backend, $last_stamp) = split (/:/, $db{$ip});
+ msg ("IP $ip had last connected on $last_stamp to $last_backend\n");
+ if ($stamp < $last_stamp + $timeout) {
+ msg ("Timeout not yet exceeded, this may be a reconnect\n");
+ # We'll allow a reconnect only if the stated last_backend is
+ # free (sanity check).
+ if (inarray ($last_backend, @backends)) {
+ msg ("Last back end $last_backend is available, ",
+ "letting through\n");
+ reply ($last_backend);
+ } else {
+ msg ("Last used back end isn't free, suggesting a new one\n");
+ }
+ } else {
+ msg ("Timeout exceeded, suggesting a new back end\n");
+ }
+ } else {
+ msg ("Np preveious connection data, suggesting a new back end\n");
+ }
+
+ my $bestweight = -1;
+ my $bestbackend;
+ for (my $i = 0; $i <= $#weights; $i++) {
+ if ($bestweight == -1 or $bestweight > $weights[$i]) {
+ $bestweight = $weights[$i];
+ $bestbackend = $backends[$i];
+ }
+ }
+
+ msg ("Best back end: $bestbackend (given weight $bestweight)\n");
+ reply ($bestbackend);
+}
+
+# Main starts here
+# ----------------
+msg ("Start of run, attaching GDBM database '$cdb'\n");
+tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600);
+
+# The first argument must be an action 'dispatch', 'start' or 'end'.
+# Depending on the action, we do stuff.
+my $action = shift (@ARGV);
+if ($action eq 'dispatch') {
+ dispatch (@ARGV);
+} elsif ($action eq 'start') {
+ start (@ARGV);
+} elsif ($action eq 'end') {
+ end (@ARGV);
+} else {
+ print STDERR ("Usage: rdp-helper {dispatch|start|end} args\n");
+ exit (1);
+})
+
+
subsect(HTTP Session Stickiness)
This section focuses on HTTP session stickiness. This term refers to
@@ -285,7 +733,7 @@ Below is a short example of a configuration.
verb(\
service www {
port 80;
- type stickyhttp;
+ type http;
revivinginterval 15;
dispatchmode byconnections;
@@ -501,6 +949,131 @@ client ----<----<----<---< crossroads ====<====<====<
analyze the output and to compute round trip times. Such scripts
are not (yet) included in Crossroads.
+subsect(Limiting Access to Crossroads by Client IP Address)
+
+subsubsect(General Examples)
+
+The directives tt(allowfrom), tt(denyfrom), tt(allowfile) and
+tt(denyfile) can be used to instruct Crossroads to specifically allow
+access by using a "whitelist" of IP addresses, or to specifically deny
+access by using a "blacklist". E.g., the following configuration
+allows access to service tt(webproxy) only to em(localhost):
+
+verb(\
+service webproxy {
+ port 8000;
+ allowfrom 127.0.0.1;
+ backend one {
+ .
+ . Back end definitions occur here
+ .
+ }
+ .
+ . Other back ends or other service directives
+ . may occur here
+ .
+})
+
+In this example there is a "whitelist" having only one entry: IP
+address 127.0.0.1, or em(localhost). (Incidentally, the same behaviour
+could be accomplished by stating em(bindto 127.0.0.1), in which case
+Crossroads would only listen to the local network device.)
+
+In the same vein, the directive tt(allowfrom 127.0.0.1 192.168.1/24)
+would allow access to em(localhost) and to all IP addresses that start
+with 192.168.1. The specifier tt(192.168.1/24) states that there are
+three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are
+relevant; so that the fourth network byte doesn't matter.
+
+subsubsect(Using External Files)
+
+The directives tt(allowfile) and tt(denyfile) allow you to specify IP
+addresses in external files. The Crossroads configuration states
+e.g. tt(allowfile /tmp/allow.txt), and the IP addresses are then in
+tt(/tmp/allow.txt). The format of tt(/tmp/allow.txt) is as follows:
+
+itemization(
+ it() The specifications follow again em(p.q.r.s/mask), where
+ p, q, r and s are network bytes which can be left out on the
+ right hand side when the mask allows it;
+
+ it() The specifications must be separated by white space
+ (spaces, tabs or newlines).)
+
+E.g., the following is a valid example of an external specification
+file:
+
+verb(\
+127.0.0.1
+192.168.1/24
+10/8)
+
+When external files are in effect, then the signal tt(SIGHUP) (1)
+causes Crossroads to reload the external file. E.g., while Crossroads
+is running, you may edit tt(/tmp/allow.txt), and then issue tt(killall
+-1 crossroads). The new contents of tt(/tmp/allow.txt) will be
+reloaded.
+
+subsubsect(Mixing Directives)
+
+Crossroads allows to mix all directives in one service
+description. However, some mixes are less meaningful than others. It's
+up to you to take this into account.
+
+The following rules apply:
+
+itemization(
+ it() Blacklisting and whitelisting can be used together. When
+ combined, the blacklist will always be interpreted
+ first. E.g., consider the following directives:
+
+ verb(\
+allowfrom 192.168.1/24
+denyfrom 192.168.1.100)
+
+ Given the fact that the deny list is checked first, client
+ 192.168.1.100 won't be able to access Crossroads. Then the
+ allow list will be checked, stating that all clients whose IP
+ address starts with 192.168.1 may connect. The effect will be
+ that e.g., client 192.168.1.1 may connect, 192.168.1.2 may
+ connect too, 192.168.1.100 will be blocked, and 10.1.1.1 will
+ be blocked as well.
+
+ Now consider the following directives:
+
+ verb(\
+allowfrom 192.168.1.100 127.0.0.1
+denyfrom 192.168.1/24)
+
+ This will first of all deny access to all IP addresses that
+ start with 192.168.1. So the rule that allows 192.168.1.100
+ won't ever be effective. The net result will be that access
+ will be granted to 127.0.0.1, and IP addresses that don't
+ match 192.168.1/24.
+
+ it() Blacklisting or whitelisting can be left out.
+ A list is considered empty when no appropriate directives
+ occur in tt(/etc/crossroads.conf), or when the directive
+ points to an empty or non-existent external file.
+
+ it() Using tt(*from) and tt(*file) statements is allowed, but
+ doesn't make sense. E.g., the following configuration sample
+ is such a case:
+
+ verb(\
+allowfrom 127.0.0.1 192.168.1/24
+allowfile /tmp/allow.txt)
+
+ There is a technical reason for this. Once Crossroads
+ processes the tt(allowfile) directive, then the whole
+ whitelist is cleared (thereby removing the entries 127.0.0.1
+ and 192.168.1/24), and new entries are reloaded from the
+ file. The net result is that the tt(allowfrom) specification
+ is overruled.
+
+ Crossroads doesn't check for such configurations, which are
+ syntactially correct, but make no semantic sense.)
+
subsect(Configuration examples)
diff --git a/doc/using.yo b/doc/using.yo
@@ -77,4 +77,15 @@ itemization(
it() Now start tt(crossroads) with the flag tt(-l7).
it() Finally, monitor tt(/var/log/crossroads.log) for Crossroads'
- messages.)
-\ No newline at end of file
+ messages.)
+
+
+subsect(Reloading Configurations)
+
+Crossroads doesn't support the reloading of a configuration while
+running (such as other programs, e.g. Apache do). There are various
+technical reasons for this.
+
+However, external lists of allowed or denied IP addresses can be
+reloaded by sending a signal -1 (tt(SIGHUP)) to Crossroads. See
+section ref(servicedef) for the details.
+\ No newline at end of file
diff --git a/etc/Makefile.def b/etc/Makefile.def
@@ -3,7 +3,7 @@
# Versioning. This defines the overall version ID and must match the topmost
# entry in the ChangeLog.
-VER = 1.17
+VER = 1.23
# Default config
DEFAULT_CONF = /etc/crossroads.conf
diff --git a/etc/dispatcher-roundrobin b/etc/dispatcher-roundrobin
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+use strict;
+
+# Example of a round-robin external dispatcher. This is totally
+# superfluous, Crossroads has this on-board; if you use the external
+# program for determining round-robin dispatching, then you'll only
+# slow things down. This script is just meant as an example.
+
+# Globals / configuration
+# -----------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $statefile = '/tmp/rr.last'; # Where we keep the last used
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+}
+
+# Read the last used back end
+# ---------------------------
+sub readlast() {
+ my $ret;
+
+ if (open (my $if, $statefile)) {
+ $ret = <$if>;
+ chomp ($ret);
+ close ($if);
+ msg ("Last used back end: $ret\n");
+ return ($ret);
+ }
+ msg ("No last-used back end (yet)\n");
+ return (undef);
+}
+
+# Write back the last used back end, reply to Crossroads and stop
+# ---------------------------------------------------------------
+sub reply ($) {
+ my $last = shift;
+
+ if (open (my $of, ">$statefile")) {
+ print $of ("$last\n");
+ }
+ print ("$last\n");
+ exit (0);
+}
+
+# Main starts here
+# ----------------
+
+# Collect the cmdline arguments. We expect pairs of backend-name /
+# backend-availablility, and we'll store only the available ones.
+msg ("Dispatch request received\n");
+my @backend;
+for (my $i = 0; $i <= $#ARGV; $i += 2) {
+ push (@backend, $ARGV[$i]) if ($ARGV[$i + 1]);
+}
+msg ("Available back ends: @backend\n");
+
+# Let's see what the last one is. If none found, then we return the
+# first available back end. Otherwise we need to go thru the list of
+# back ends, and return the next one in line.
+my $last = readlast();
+if ($last eq '') {
+ msg ("Returning first available back end $backend[0]\n");
+ reply ($backend[0]);
+}
+
+# There **was** a last back end. Try to match it in the list,
+# then return the next-in-line.
+for (my $i = 0; $i < $#backend; $i++) {
+ if ($last eq $backend[$i]) {
+ msg ("Returning next back end ", $backend[$i + 1], "\n");
+ reply ($backend[$i + 1]);
+ }
+}
+
+# No luck.. run back to the first one.
+msg ("Returning first back end $backend[0]\n");
+reply ($backend[0]);
+
diff --git a/etc/hdr.template b/etc/hdr.template
@@ -0,0 +1,5 @@
+/*************************************************************************
+ * This file is part of Crosroads __VER__, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
diff --git a/etc/rdp-helper b/etc/rdp-helper
@@ -0,0 +1,140 @@
+#!/usr/bin/perl
+
+use strict;
+use GDBM_File;
+
+# Global variables and configuration
+# ----------------------------------
+my $log = '/tmp/exthandler.log'; # Debug log, set to /dev/null to suppress
+my $cdb = '/tmp/client.db'; # GDBM database of clients
+my %db; # .. and memory representation of it
+my $timeout = 24*60*60; # Timeout of a connection in secs
+
+# Logging
+# -------
+sub msg {
+ return if ($log eq '/dev/null' or $log eq '');
+ open (my $of, ">>$log") or return;
+ print $of (scalar(localtime()), ' ', @_);
+ close ($of);
+}
+
+# Reply a back end to the caller and stop processing.
+# ---------------------------------------------------
+sub reply ($) {
+ my $b = shift;
+ msg ("Suggesting $b to Crossroads.\n");
+ print ("$b\n");
+ exit (0);
+}
+
+# Is a value in an array
+# ----------------------
+sub inarray {
+ my $val = shift;
+ for my $other (@_) {
+ return (1) if ($other eq $val);
+ }
+ return (0);
+}
+
+# A connection is starting
+# ------------------------
+sub start {
+ my ($ip, $stamp, $backend) = @_;
+ msg ("Logging START of connection for IP $ip on stamp $stamp, ",
+ "back end $backend\n");
+ $db{$ip} = "$backend:$stamp";
+}
+
+# A connection has ended
+# ----------------------
+sub end {
+ my $ip = shift;
+ msg ("Logging END of connection for IP $ip\n");
+ $db{$ip} = undef;
+}
+
+# A back end has failed
+# ---------------------
+sub fail {
+ my $backend = shift;
+ msg ("Back end $backend failed, thanks for notifying me\n");
+}
+
+# Request to determine a back end
+# -------------------------------
+sub dispatch {
+ my $ip = shift;
+ my $stamp = shift;
+
+ msg ("Request to dispatch IP $ip on stamp $stamp\n");
+
+ # Read the next arguments. They are triplets of
+ # backend-name / availability / weight. Store if the back end is
+ # available.
+ my (@backends, @weights);
+ for (my $i = 0; $i < $#_; $i += 3) {
+ if ($_[$i + 1] != 0) {
+ push (@backends, $_[$i]);
+ push (@weights, $_[$i + 2]);
+ msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n");
+ }
+ }
+
+ # See if this is a reconnect by a previously seen client IP. We'll
+ # treat this as a reconnect if the timeout wasn't yet exceeded.
+ if ($db{$ip} ne '') {
+ my ($last_backend, $last_stamp) = split (/:/, $db{$ip});
+ msg ("IP $ip had last connected on $last_stamp to $last_backend\n");
+ if ($stamp < $last_stamp + $timeout) {
+ msg ("Timeout not yet exceeded, this may be a reconnect\n");
+ # We'll allow a reconnect only if the stated last_backend is
+ # free (sanity check).
+ if (inarray ($last_backend, @backends)) {
+ msg ("Last back end $last_backend is available, ",
+ "letting through\n");
+ reply ($last_backend);
+ } else {
+ msg ("Last used back end isn't free, suggesting a new one\n");
+ }
+ } else {
+ msg ("Timeout exceeded, suggesting a new back end\n");
+ }
+ } else {
+ msg ("Np preveious connection data, suggesting a new back end\n");
+ }
+
+ my $bestweight = -1;
+ my $bestbackend;
+ for (my $i = 0; $i <= $#weights; $i++) {
+ if ($bestweight == -1 or $bestweight > $weights[$i]) {
+ $bestweight = $weights[$i];
+ $bestbackend = $backends[$i];
+ }
+ }
+
+ msg ("Best back end: $bestbackend (given weight $bestweight)\n");
+ reply ($bestbackend);
+}
+
+# Main starts here
+# ----------------
+msg ("Start of run, attaching GDBM database '$cdb'\n");
+tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600);
+
+# The first argument must be an action 'dispatch', 'start' or 'end'.
+# Depending on the action, we do stuff.
+my $action = shift (@ARGV);
+if ($action eq 'dispatch') {
+ dispatch (@ARGV);
+} elsif ($action eq 'start') {
+ start (@ARGV);
+} elsif ($action eq 'end') {
+ end (@ARGV);
+} elsif ($action eq 'fail') {
+ fail (@ARGV);
+} else {
+ print STDERR ("Usage: rdp-helper {dispatch|start|end|fail} args\n");
+ exit (1);
+}
diff --git a/src/Makefile b/src/Makefile
@@ -18,14 +18,10 @@ samplerun: all
-./crossroads -c sample.conf stop
./crossroads -c sample.conf start
-textconv: usage.h sampleconf.h proxyerror.h
+textconv: usage.h proxyerror.h
usage.h: usage.txt
../tools/untab usage.txt
../tools/e-txt2c USAGETEXT <usage.txt >usage.h
-sampleconf.h: sample.conf
- ../tools/untab sample.conf
- ../tools/e-txt2c SAMPLECONF <sample.conf >sampleconf.h
- ../tools/untab sampleconf.h
proxyerror.h: proxyerror.txt
../tools/untab proxyerror.txt
../tools/e-txt2c ERRORTEXT <proxyerror.txt >proxyerror.h
@@ -53,7 +49,8 @@ DEFS = -DDEFAULT_CONF=\"$(DEFAULT_CONF)\" -DMAX_BACKEND=$(MAX_BACKEND) \
$(shell ../tools/c-conf libfunction lockf HAVE_LOCKF) \
$(shell ../tools/c-conf libfunction strlcat HAVE_STRLCAT) \
$(shell ../tools/c-conf libfunction sranddev HAVE_SRANDDEV) \
- $(shell ../tools/c-conf libfunction vsyslog HAVE_VSYSLOG)
+ $(shell ../tools/c-conf libfunction vsyslog HAVE_VSYSLOG) \
+ $(shell ../tools/c-conf libfunction strcasestr HAVE_STRCASESTR)
LIBS = $(shell ../tools/c-conf lib ucb nsl pthread socket 2>/dev/null)
@@ -75,7 +72,7 @@ libcrossroads.a: $(OBJ)
distclean: clean
clean:
rm -f $(OBJ) libcrossroads.a crossroads \
- usage.h sampleconf.h proxyerror.h
+ usage.h proxyerror.h
# Extra deps:
usage.o: usage.c usage.h ../etc/Makefile.def
diff --git a/src/allocreporter.c b/src/allocreporter.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void alloc_reporter (Service *s, int first) {
@@ -57,8 +62,4 @@ void alloc_reporter (Service *s, int first) {
if ( ((servicereport = shmat (shmid, 0, 0))) == (void *) -1 )
error ("Cannot attach shared memory for service %s: %s",
s->name, strerror(errno));
-
- /* Zero it out if this is the first usage. */
- if (first)
- memset (servicereport, 0, sizeof(Servicereport));
}
diff --git a/src/ansistamp.c b/src/ansistamp.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *ansistamp (TmType t) {
diff --git a/src/backendavailable.c b/src/backendavailable.c
@@ -1,29 +1,26 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
-int backend_available () {
- int i;
-
- /* Just see if all back ends are flagged down. In that case
- * we return 0.
- * We won't actually connect here with a back end, tcp_serve()
- * will do that later on.
+int backend_available (int target) {
+ /* Backend is available when:
+ * (a) The state indicates availability;
+ * (b) Either, there is no max to the active connections, or
+ * the # of connections is below the max.
*/
+ if (servicereport->backendstate[target].avail == st_available &&
+ (activeservice->backend[target].maxconnections == 0 ||
+ activeservice->backend[target].maxconnections >
+ servicereport->backendstate[target].nclients)) {
+ msg ("Service %s: target nr %d is available",
+ activeservice->name, target);
+ return (1);
+ }
- for (i = 0; i < activeservice->nbackend; i++) {
- /* Backend i is available when:
- * (a) The state indicates availability;
- * (b) Either, there is no max to the active connections, or
- * the # of connections is below the max.
- */
- if (servicereport->backendstate[i].avail == st_available &&
- (activeservice->backend[i].maxconnections == 0 ||
- activeservice->backend[i].maxconnections >
- servicereport->backendstate[i].nclients))
- msg ("Service %s: found (at least) one available back end",
- activeservice->name);
- return (1);
- }
-
- msg ("Found no available back end!");
+ msg ("Service %s: target nr %d is unavailable",
+ activeservice->name, target);
return (0);
}
diff --git a/src/backendconnect.c b/src/backendconnect.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
static jmp_buf jmpbuf;
@@ -5,6 +10,10 @@ static int timeout;
static void alarmhandler (int sig) {
alarm(0);
+ timeout++;
+ if (program_stage != stage_retrying)
+ msg ("Service %s: Alarm went off while connecting to back end",
+ activeservice->name);
longjmp (jmpbuf, 1);
}
@@ -36,6 +45,11 @@ int backend_connect () {
mark_activity (0, 0, st_unavailable);
return (-2);
}
+ if (program_stage != stage_retrying)
+ msg ("Service %s: Network socket to %s:%d initialized",
+ activeservice->name,
+ activeservice->backend[current_backend].server,
+ activeservice->backend[current_backend].port);
signal (SIGALRM, alarmhandler);
timeout = 0;
@@ -45,18 +59,21 @@ int backend_connect () {
/* First time around, try connecting */
if (connect (backend_sock, (struct sockaddr *) &servername,
sizeof(servername)) < 0) {
- /* This backend is unusable..
+ /* This backend is unusable.
* Either connect() has failed (connection refused) or it got
* interrupted by the SIGALRM
*/
close (backend_sock);
alarm(0);
- mark_activity (0, 0, st_unavailable);
+ signal (SIGALRM, SIG_DFL);
+
if (timeout) {
if (program_stage != stage_retrying)
- warning ("Service %s: server %s not usable due to timeout",
+ warning ("Service %s: server %s:%d not usable"
+ " due to timeout",
activeservice->name,
- activeservice->backend[current_backend].server);
+ activeservice->backend[current_backend].server,
+ activeservice->backend[current_backend].port);
} else {
if (program_stage != stage_retrying)
warning ("Service %s: server %s:%d: cannot connect: %s",
@@ -64,23 +81,30 @@ int backend_connect () {
activeservice->backend[current_backend].server,
activeservice->backend[current_backend].port,
strerror(errno));
- mark_activity (0, 0, st_unavailable);
}
+ mark_activity (0, 0, st_unavailable);
return (-3);
+ } else {
+ msg ("Service %s: connection to back end %s succeeded",
+ activeservice->name,
+ activeservice->backend[current_backend].server);
}
- alarm(0);
} else {
/* Got here because of longjmp from the signal handler,
* this MUST be a timeout
*/
- close (backend_sock);
- mark_activity (0, 0, st_unavailable);
if (program_stage != stage_retrying)
- warning ("Server %s not usable due to timeout",
+ warning ("Service %s: Server %s not usable due to timeout",
+ activeservice->name,
activeservice->backend[current_backend].server);
+ close (backend_sock);
+ alarm(0);
+ signal (SIGALRM, SIG_DFL);
+ mark_activity (0, 0, st_unavailable);
return (-4);
}
+ alarm(0);
signal (SIGALRM, SIG_DFL);
return (backend_sock);
}
diff --git a/src/backendcount.c b/src/backendcount.c
@@ -0,0 +1,16 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int backend_count () {
+ int n = 0, i;
+
+ for (i = 0; i < activeservice->nbackend; i++)
+ if (backend_available(i))
+ n++;
+ msg ("Service %s: %d backend(s) available", activeservice->name, n);
+ return (n);
+}
diff --git a/src/choosebackend.c b/src/choosebackend.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void choose_backend () {
@@ -6,9 +11,12 @@ void choose_backend () {
tot_weights, lo_val, hi_val, done;
unsigned long long values[MAX_BACKEND], nbest;
unsigned nclients;
+ char *exthandler;
+ FILE *f;
+ char buf[80];
# ifndef HAVE_SRANDDEV
struct timeval tv;
-# endif
+# endif
/* Check that we're allowed to accept at all. */
if (activeservice->maxconnections &&
@@ -64,13 +72,15 @@ void choose_backend () {
case ds_roundrobin:
/* Find the currently used backend, go one forward. */
+ msg ("Service %s: last roundrobin back end was %d",
+ activeservice->name, servicereport->last_backend);
for (i = 0; i < nbackends; i++)
if (backends[i] == servicereport->last_backend) {
i++;
i %= nbackends;
current_backend = backends[i];
servicereport->last_backend = current_backend;
- msg ("Service %s: chosen backend (roundrobin): %d",
+ msg ("Service %s: next roundrobin back end is %d",
activeservice->name, current_backend);
return;
}
@@ -81,7 +91,7 @@ void choose_backend () {
"chosen backend (roundrobin, without historical info): %d",
activeservice->name, current_backend);
return;
-
+
case ds_random:
/* Re-randomize. */
# ifdef HAVE_SRANDDEV
@@ -296,6 +306,47 @@ void choose_backend () {
activeservice->name, current_backend);
return;
+ case ds_externalhandler:
+ /* External handler to be called */
+ exthandler = str_expand_format (activeservice->dispatchext);
+ msg ("Service %s: calling external handler '%s'",
+ activeservice->name, exthandler);
+ uid_assume();
+ if (! (f = popen (exthandler, "r")) ) {
+ warning ("Service %s: failed to start external handler '%s': %s",
+ activeservice->name, exthandler, strerror(errno));
+ current_backend = backends[0];
+ servicereport->last_backend = current_backend;
+ uid_restore();
+ return;
+ }
+ while (1) {
+ if (fscanf (f, " %80s ", buf) < 1) {
+ msg ("Service %s: external handler signals end",
+ activeservice->name);
+ pclose (f);
+ uid_restore();
+ break;
+ }
+ msg ("Service %s: external handler says '%s'",
+ activeservice->name, buf);
+ for (i = 0; i < nbackends; i++)
+ if (!strcmp (buf, activeservice->backend[backends[i]].name)) {
+ msg ("Service %s: selecting back end %s due to "
+ "external handler", activeservice->name, buf);
+ current_backend = backends[i];
+ servicereport->last_backend = current_backend;
+ pclose (f);
+ uid_restore();
+ return;
+ }
+ }
+ warning ("Service %s: external handler didn't reply with "
+ "a selectable back end", activeservice->name);
+ current_backend = backends[0];
+ servicereport->last_backend = current_backend;
+ return;
+
default:
/* Internal fry.. */
error ("Service %s: internal error: unhandled dispatch type %d",
diff --git a/src/configtest.c b/src/configtest.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int configtest (int ac, char **av) {
diff --git a/src/copysockets.c b/src/copysockets.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void copysockets (int clientsock, int serversock) {
diff --git a/src/createcommandlinespace.c b/src/createcommandlinespace.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void create_commandline_space () {
diff --git a/src/crossroads.h b/src/crossroads.h
@@ -1,3 +1,9 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
/* Includes. */
#include <ctype.h>
@@ -63,8 +69,9 @@ typedef enum { /* Config parsing related */
cf_shmkeyspec,
cf_weightspec,
cf_decayspec,
- cf_onsuccspec,
+ cf_onstartspec,
cf_onfailspec,
+ cf_onendspec,
cf_connectiontimeoutspec,
cf_maxconnectionsspec,
cf_typespec,
@@ -78,6 +85,11 @@ typedef enum { /* Config parsing related */
cf_setserverheaderspec,
cf_addserverheaderspec,
cf_appendserverheaderspec,
+ cf_allowfromspec,
+ cf_denyfromspec,
+ cf_allowfilespec,
+ cf_denyfilespec,
+ cf_useraccountspec,
} Conftype;
typedef union { /* Integer of string value */
@@ -102,6 +114,7 @@ typedef enum { /* Dispatching types */
ds_byduration,
ds_byorder,
ds_byconnections,
+ ds_externalhandler,
} Dispatchtype;
typedef enum { /* Service types */
@@ -116,8 +129,9 @@ typedef struct { /* Backend description */
int verbosity; /* .. debugging on/off */
int weight; /* .. weight in backend selection */
int decay; /* .. decay in % */
- char *onsuccess; /* .. system() on success */
- char *onfailure; /* .. or on failure */
+ char *onstart; /* .. system() on success */
+ char *onfail; /* .. or on failure */
+ char *onend; /* .. or on ending */
char *dumpfile; /* .. traffic dump file */
char *thruputfile; /* .. traffic throughput file */
int maxconnections; /* .. max # of allowed connections */
@@ -137,9 +151,8 @@ typedef struct { /* Backend description */
} Backend;
typedef struct { /* Filtering information: */
- unsigned char ip[4]; /* .. IP address (or part of it) */
- unsigned char ip_len; /* .. used # of bytes in ip */
- unsigned char mask; /* .. netmask */
+ unsigned ip; /* .. IP address (or part of it) */
+ unsigned mask; /* .. netmask */
} IpFilter;
typedef struct { /* Service description */
@@ -149,6 +162,7 @@ typedef struct { /* Service description */
unsigned verbosity; /* .. message generation */
Dispatchtype dispatchtype; /* .. backend selection method */
unsigned dispatchover; /* .. selection over # connections */
+ char *dispatchext; /* .. external handler */
unsigned rev_interval; /* .. dead backend revival interval */
unsigned backlog; /* .. # pending TCP connections */
unsigned shmkey; /* .. key for SysV IPC */
@@ -159,6 +173,12 @@ typedef struct { /* Service description */
int nbackend; /* .. size of backend array */
char *allowfile; /* .. file with allowed IP filters */
char *denyfile; /* .. and denied */
+ IpFilter *allowchain; /* .. chain of allowed IP's */
+ int nallowchain; /* .. size of chain */
+ IpFilter *denychain; /* .. chain of denied IP's */
+ int ndenychain; /* .. size of chain */
+ int uid; /* .. UID to assume for system() */
+ int gid; /* .. GID to assume for system() */
} Service;
typedef enum { /* Backend availability */
@@ -233,6 +253,7 @@ EXTERN char *config_file; /* config to parse */
EXTERN int current_backend; /* of a given service */
EXTERN int daemonized; /* are we forked off yet */
EXTERN int flag_verbose; /* flag: verbosity */
+EXTERN int gid_org, gid_set; /* original gid, flag: changed? */
EXTERN int iflag_present; /* flag: -i present */
EXTERN int interrupted; /* got a signal? */
EXTERN char *laststring; /* semantic lexer value */
@@ -252,6 +273,7 @@ EXTERN int sloppyportbind; /* -s flag present */
EXTERN unsigned char *srbuf; /* server socket input buffer */
EXTERN unsigned srbufpos, srbufmax; /* .. position & bytes */
EXTERN char *state_to_string_map[]; /* backend states as strings */
+EXTERN int uid_org, uid_set; /* original uid, flag: changed? */
EXTERN FILE *yyin; /* config file handle */
EXTERN int yylineno; /* input line number */
EXTERN char *yyerrmsg; /* parsing error msg */
@@ -260,7 +282,8 @@ EXTERN char *yytext; /* lexical buffer */
/* Functions. */
extern void alloc_reporter (Service *active, int first);
extern char *ansistamp (TmType t);
-extern int backend_available (void);
+extern int backend_available (int target);
+extern int backend_count(void);
extern int backend_connect (void);
extern void choose_backend (void);
extern void copysockets (int clientsock, int serversock);
@@ -273,7 +296,6 @@ extern int fork_tcp_servicer (int to_backend);
extern void http_copy (HttpHeader *h, int src_sock, int dst_sock,
CopyDirection dir);
extern void http_error (int clientsock);
-extern char *http_expand_header (char const *h);
extern void http_header_addheader (HttpHeader *m, char const *hdr);
extern void http_header_appendheader (HttpHeader *m, char const *hdr);
extern HttpConnectionType http_header_connectiontype (HttpHeader *h);
@@ -291,11 +313,18 @@ extern int http_serversocket (HttpHeader *m, int *is_continuation);
extern int http_write (int sock, CopyDirection dir, unsigned char const *buf,
unsigned buflen);
extern void incr_client_count (void);
-extern int is_hex_digit (char ch);
-extern int is_space (char ch);
extern int init_sockaddr (struct sockaddr_in *name,
const char *hostname, int port);
extern void interrupt (int sig);
+extern int ipf_add_allow (Service *s, char const *val);
+extern int ipf_add_deny (Service *s, char const *val);
+extern int ipf_allowed (void);
+extern int ipf_denied (void);
+extern int ipf_loadfile (char const *fname, IpFilter **chain, int *nchain);
+extern int ipf_match (IpFilter f);
+extern int ipf_parse (char const *val, IpFilter *res);
+extern int is_hex_digit (char ch);
+extern int is_space (char ch);
extern void lock_reporter (void);
extern void log_activity_any (char const *action);
extern void log_activity_start (void);
@@ -318,7 +347,6 @@ extern int net_write (int sock, unsigned char const *buf, unsigned len,
int is_client);
extern int restart (int ac, char **av);
extern void runservice (void);
-extern int sample_conf (int ac, char **av);
extern int serve (int ac, char **av);
extern void set_program_title (char const *fmt, ...);
extern int show_services (int ac, char **av);
@@ -327,6 +355,7 @@ extern char *stage_to_string (Programstage stage);
extern char *state_to_string (Backendavail avail);
extern Backendavail string_to_state (char const *str);
extern int stop_daemon (int ac, char **av);
+extern char *str_expand_format (char const *h);
extern char *str_printf (char const *fmt, ...);
extern char *str_vprintf (char const *fmt, va_list args);
extern void sysrun (char const *cmd);
@@ -335,6 +364,8 @@ extern int tell_service (int ac, char **av);
extern void thruputlog (unsigned char const *buf, int len, CopyDirection dir);
extern void trafficlog (unsigned char const *buf, int len, CopyDirection dir);
extern void unlock_reporter (void);
+extern void uid_assume (void);
+extern void uid_restore (void);
extern void usage (void);
extern void wakeup_handler (void);
extern void warning (char const *fmt, ...);
@@ -355,3 +386,8 @@ extern size_t strlcat (char *dst, char const *src, size_t size);
#ifndef HAVE_VSYSLOG
extern void vsyslog (int fac, char const *fmt, va_list args);
#endif
+
+/* strcasestr() if it's missing on this system */
+#ifndef HAVE_STRCASESTR
+extern char *strcasestr (char const *big, char const *little);
+#endif
diff --git a/src/deallocreporter.c b/src/deallocreporter.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void dealloc_reporter (Service *s) {
diff --git a/src/decrclientcount.c b/src/decrclientcount.c
@@ -1,8 +1,28 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
#include "crossroads.h"
void decr_client_count () {
- lock_reporter();
- servicereport->nclients--;
- servicereport->backendstate[current_backend].nclients--;
- unlock_reporter();
+ unsigned totclients = 0;
+ int i;
+
+ if (program_stage != stage_serving)
+ return;
+
+ if (servicereport->backendstate[current_backend].nclients > 0) {
+ lock_reporter();
+ servicereport->backendstate[current_backend].nclients--;
+ for (i = 0; i < activeservice->nbackend; i++)
+ totclients +=
+ servicereport->backendstate[i].nclients;
+ servicereport->nclients = totclients;
+ msg ("Service %s: total active clients now %u",
+ activeservice->name, totclients);
+ unlock_reporter();
+ }
+ sysrun (activeservice->backend[current_backend].onend);
}
diff --git a/src/error.c b/src/error.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void error (char const *fmt, ...) {
diff --git a/src/forktcpservicer.c b/src/forktcpservicer.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int fork_tcp_servicer (int to_backend) {
@@ -22,11 +27,13 @@ int fork_tcp_servicer (int to_backend) {
program_stage = stage_serving;
if (to_backend > 0)
- set_program_title ("Service %s: serving, back end is %s",
+ set_program_title ("Service %s: serving %s to %s",
+ client_ip,
activeservice->name,
activeservice->backend [current_backend].name);
else
- set_program_title ("Service %s: serving",
+ set_program_title ("Service %s: serving %s",
+ client_ip,
activeservice->name);
return (0);
}
diff --git a/src/httpcopy.c b/src/httpcopy.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
static void copy (int src, int dst, int dir, unsigned tot) {
diff --git a/src/httperror.c b/src/httperror.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
#include "proxyerror.h"
@@ -8,5 +13,6 @@ void http_error (int clientsock) {
ERRORTEXT,
strlen(ERRORTEXT));
write (clientsock, buf, strlen(buf));
+ decr_client_count();
error ("No back end could be selected. Client received error page.");
}
diff --git a/src/httpexpandheader.c b/src/httpexpandheader.c
@@ -1,41 +0,0 @@
-#include "crossroads.h"
-
-char *http_expand_header (char const *h) {
- char *ret = 0;
- char const *cp;
-
- for (cp = h; cp && *cp; cp++) {
- if (*cp == '%') {
- switch (*(cp + 1)) {
- case 'r':
- ret = xstrcat (ret, client_ip);
- cp++;
- break;
- case 't':
- ret = xstrcat (ret, ansistamp (tm_localtime));
- cp++;
- break;
- case 'T':
- ret = xstrcat (ret, ansistamp (tm_gmtime));
- cp++;
- break;
- case 'v':
- ret = xstrcat (ret, VER);
- cp++;
- break;
- default:
- if (*(cp + 1)) {
- ret = xstrcatch (ret, *(cp + 1));
- cp++;
- }
- break;
- }
- } else {
- ret = xstrcatch (ret, *cp);
- }
- }
-
- return (ret);
-}
-
-
diff --git a/src/httpheaderaddheader.c b/src/httpheaderaddheader.c
@@ -1,9 +1,14 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_addheader (HttpHeader *m, char const *h) {
char *exp;
- exp = http_expand_header (h);
+ exp = str_expand_format (h);
msg ("Service %s: adding header '%s'", activeservice->name, exp);
m->header = xrealloc (m->header, (m->nheader + 1) * sizeof(char*));
m->header[m->nheader++] = exp;
diff --git a/src/httpheaderappendheader.c b/src/httpheaderappendheader.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_appendheader (HttpHeader *m, char const *h) {
@@ -14,7 +19,7 @@ void http_header_appendheader (HttpHeader *m, char const *h) {
while (is_space (*cp) || *cp == ':')
cp++;
- exp = http_expand_header (cp);
+ exp = str_expand_format (cp);
for (i = 0; i < m->nheader; i++) {
if (!m->header[i] || !*m->header[i])
diff --git a/src/httpheaderconnectiontype.c b/src/httpheaderconnectiontype.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
HttpConnectionType http_header_connectiontype (HttpHeader *h) {
@@ -11,11 +16,11 @@ HttpConnectionType http_header_connectiontype (HttpHeader *h) {
"no such header", activeservice->name);
return (con_unknown);
}
- if (!strncasecmp ((char const *)val, "close", 5)) {
+ if (strcasestr ((char const *)val, "close")) {
msg ("Service %s: Connection-Type is CLOSE",
activeservice->name);
return (con_close);
- } else if (!strncasecmp ((char const *)val, "keep-alive", 10)) {
+ } else if (strcasestr ((char const *)val, "keep-alive")) {
msg ("Service %s: Connection-Type is KEEP-ALIVE",
activeservice->name);
return (con_keepalive);
diff --git a/src/httpheaderfree.c b/src/httpheaderfree.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_free (HttpHeader *m) {
diff --git a/src/httpheaderhascookie.c b/src/httpheaderhascookie.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int http_header_hascookie (HttpHeader *m, char const *cookie) {
diff --git a/src/httpheaderhttpver.c b/src/httpheaderhttpver.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
double http_header_httpver (HttpHeader *h) {
diff --git a/src/httpheadernew.c b/src/httpheadernew.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
HttpHeader *http_header_new () {
diff --git a/src/httpheaderread.c b/src/httpheaderread.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
// #define DEBUG
diff --git a/src/httpheaderremoveheader.c b/src/httpheaderremoveheader.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_removeheader (HttpHeader *h, char const *hdr) {
diff --git a/src/httpheadersetheader.c b/src/httpheadersetheader.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_setheader (HttpHeader *m, char const *h) {
@@ -11,7 +16,7 @@ void http_header_setheader (HttpHeader *m, char const *h) {
}
*cp = 0;
- exp = http_expand_header (h);
+ exp = str_expand_format (h);
for (i = 0; i < m->nheader; i++) {
if (!m->header[i] || !*m->header[i])
continue;
diff --git a/src/httpheaderval.c b/src/httpheaderval.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
unsigned char const *http_header_val (HttpHeader *m, char const *what) {
diff --git a/src/httpheaderwrite.c b/src/httpheaderwrite.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_header_write (HttpHeader *m, int sock, CopyDirection dir) {
diff --git a/src/httpinsertheader.c b/src/httpinsertheader.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_insert_header (unsigned char **bufp, unsigned *buflen,
diff --git a/src/httpserve.c b/src/httpserve.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void http_serve (int clientsock) {
@@ -11,7 +16,7 @@ void http_serve (int clientsock) {
if (fork_tcp_servicer(-1))
return;
- /*
+ /* We're the child branch now of fork_tcp_servicer().
* Here's the logic of the HTTP services.
* - Get the client's request;
* - If we're not yet connected to a back end: determine the back
@@ -37,6 +42,7 @@ void http_serve (int clientsock) {
if ( (serversock = http_serversocket (clientreq,
&is_continuation)) < 1 )
http_error (clientsock);
+
incr_client_count();
if (is_continuation)
log_activity_continuation();
diff --git a/src/httpserversocket.c b/src/httpserversocket.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int http_serversocket (HttpHeader *m, int *is_continuation) {
@@ -27,6 +32,11 @@ int http_serversocket (HttpHeader *m, int *is_continuation) {
current_backend = i;
if ( (sock = backend_connect ()) >= 0 ) {
*is_continuation = 1;
+ set_program_title ("Service %s: serving %s to %s",
+ activeservice->name,
+ client_ip,
+ activeservice->backend[current_backend]
+ .name);
return (sock);
}
}
@@ -37,7 +47,7 @@ int http_serversocket (HttpHeader *m, int *is_continuation) {
* session. So: Loop 'till we find a back end, or until we fail. */
while (1) {
/* No back end? Nogo. */
- if (! backend_available()) {
+ if (! backend_count()) {
msg ("Service %s: "
"out of back ends while scanning for HTTP back end",
activeservice->name);
@@ -56,6 +66,10 @@ int http_serversocket (HttpHeader *m, int *is_continuation) {
if ( (sock = backend_connect ()) >= 0 ) {
servicereport->backendstate[current_backend].sessions++;
*is_continuation = 0;
+ set_program_title ("Service %s: serving %s to %s",
+ activeservice->name,
+ client_ip,
+ activeservice->backend[current_backend].name);
return (sock);
}
}
diff --git a/src/httpwrite.c b/src/httpwrite.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int http_write (int sock, CopyDirection dir, unsigned char const *buf,
diff --git a/src/incrclientcount.c b/src/incrclientcount.c
@@ -1,9 +1,32 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+
#include "crossroads.h"
void incr_client_count () {
- lock_reporter();
- servicereport->nclients++;
- if (current_backend >= 0)
+ unsigned totclients = 0;
+ int i;
+
+ if (program_stage != stage_serving)
+ return;
+
+ if (current_backend >= 0) {
+ lock_reporter();
servicereport->backendstate[current_backend].nclients++;
- unlock_reporter();
+ servicereport->backendstate[current_backend].totuses++;
+ for (i = 0; i < activeservice->nbackend; i++)
+ totclients +=
+ servicereport->backendstate[i].nclients;
+ servicereport->nclients = totclients;
+ unlock_reporter();
+ sysrun (activeservice->backend[current_backend].onstart);
+
+ msg ("Service %s: Activity on back end %d now %d, total %d",
+ activeservice->name,
+ servicereport->backendstate[current_backend].nclients,
+ totclients);
+ }
}
diff --git a/src/initsockaddr.c b/src/initsockaddr.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int init_sockaddr (struct sockaddr_in *name,
diff --git a/src/interrupt.c b/src/interrupt.c
@@ -1,7 +1,37 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
/* Signal handler */
void interrupt (int sig) {
+ msg ("Caught signal %d", sig);
+
+ /* SIGHUP (1) is handled differently, it will reload the allow- and
+ * deny files when in waiting mode. In other modes it will be ignored. */
+ if (sig == SIGHUP) {
+ if (program_stage == stage_waiting) {
+ if (ipf_loadfile (activeservice->allowfile,
+ &(activeservice->allowchain),
+ &(activeservice->nallowchain)))
+ warning ("Bad syntax in allow file");
+ if (ipf_loadfile (activeservice->denyfile,
+ &(activeservice->denychain),
+ &(activeservice->ndenychain)))
+ warning ("Bad syntax in deny file");
+ }
+
+ msg ("Service %s: allow chain has %d entries, "
+ "deny chain has %d entries", activeservice->name,
+ activeservice->nallowchain, activeservice->ndenychain);
+ return;
+ }
+
+ /* For all other signals: flag that we're signalled, and depending on
+ * the stage, exit right away or wait 'till the servicer stops. */
+
interrupted++;
switch (program_stage) {
diff --git a/src/ipfaddallow.c b/src/ipfaddallow.c
@@ -0,0 +1,17 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_add_allow (Service *s, char const *val) {
+ IpFilter filter;
+
+ if (ipf_parse (val, &filter))
+ return (1);
+ s->allowchain = xrealloc (s->allowchain,
+ (s->nallowchain + 1) * sizeof(IpFilter));
+ s->allowchain[s->nallowchain++] = filter;
+ return (0);
+}
diff --git a/src/ipfadddeny.c b/src/ipfadddeny.c
@@ -0,0 +1,17 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_add_deny (Service *s, char const *val) {
+ IpFilter filter;
+
+ if (ipf_parse (val, &filter))
+ return (1);
+ s->denychain = xrealloc (s->denychain,
+ (s->ndenychain + 1) * sizeof(IpFilter));
+ s->denychain[s->ndenychain++] = filter;
+ return (0);
+}
diff --git a/src/ipfallowed.c b/src/ipfallowed.c
@@ -0,0 +1,22 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_allowed () {
+ int i;
+
+ /* Always allow a connection when we ddon't have an allow list. */
+ if (! activeservice->allowchain)
+ return (1);
+
+ /* Try all in the chain. */
+ for (i = 0; i < activeservice->nallowchain; i++)
+ if (ipf_match (activeservice->allowchain[i]))
+ return (1);
+
+ /* Allow chain doesn't match */
+ return (0);
+}
diff --git a/src/ipfdenied.c b/src/ipfdenied.c
@@ -0,0 +1,22 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_denied () {
+ int i;
+
+ /* Never deny a connection when we don't have a deny list. */
+ if (! activeservice->denychain)
+ return (0);
+
+ /* Try all in the chain. */
+ for (i = 0; i < activeservice->ndenychain; i++)
+ if (ipf_match (activeservice->denychain[i]))
+ return (1);
+
+ /* Deny chain doesn't match */
+ return (0);
+}
diff --git a/src/ipfloadfile.c b/src/ipfloadfile.c
@@ -0,0 +1,53 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_loadfile (char const *fname, IpFilter **chain, int *nchain) {
+ FILE *f;
+ char buf[80];
+ int nloaded = 0, ret = 0;
+ IpFilter filter;
+
+ /* No file? No update. */
+ if (!fname || !*fname)
+ return (0);
+
+ msg ("Service %s: loading IP filter file: '%s'",
+ activeservice->name, fname);
+
+ if (! (f = fopen (fname, "r")) ) {
+ warning ("Service %s: can't read filter file '%s': %s",
+ activeservice->name, fname, strerror(errno));
+ return (0);
+ }
+
+ free (*chain);
+ *chain = 0;
+ *nchain = 0;
+
+ while (1) {
+ if (fscanf (f, " %79s ", buf) < 1)
+ break;
+ if (ipf_parse (buf, &filter)) {
+ warning ("Service %s: bad ip filter in file '%s'",
+ activeservice->name, fname);
+ ret++;
+ } else {
+ msg ("Service %s: got ip filter specifier '%s'",
+ activeservice->name, buf);
+ *chain = xrealloc (*chain, (*nchain + 1) * sizeof(IpFilter));
+ (*chain)[*nchain] = filter;
+ (*nchain)++;
+ nloaded++;
+ }
+ }
+
+ msg ("Service %s: loaded IP filter file '%s': %d successfully loaded",
+ activeservice->name, fname, nloaded);
+
+ fclose (f);
+ return (ret);
+}
diff --git a/src/ipfmatch.c b/src/ipfmatch.c
@@ -0,0 +1,43 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_match (IpFilter f) {
+ static int client_ip_nr;
+ int counter, val;
+ char *tmp, *cp;
+
+ /* Build up the actual client IP as an int.
+ * We need to do this in reverse order; e.g. 1.2.3.4 needs to become
+ * 4<<24 | 3<<16 | 2<<8 | 1. Reason is that when mask /16 applies,
+ * we can OR this value with 16, and we'll keep 2<<8 | 1, the only
+ * significant digits. */
+ if (!client_ip_nr) {
+ tmp = xstrdup (client_ip);
+ counter = 0;
+ for (counter = 0, cp = strtok (tmp, ".");
+ cp;
+ counter += 8, cp = strtok (0, ".")) {
+ if (sscanf (cp, "%d", &val) < 1) {
+ free (tmp);
+ return (1);
+ }
+ val <<= counter;
+ client_ip_nr |= val;
+ /* msg ("ipf_match: val 0x%x, client_ip_nr 0x%x",
+ * val, client_ip_nr); */
+ }
+ }
+ free (tmp);
+
+ /* Here's the comparison. */
+ /* msg ("ipf_match: filter ip/mask 0x%x/0x%x, "
+ * "filter res 0x%x, client res 0x%x",
+ * f.ip, f.mask,
+ * (f.ip & f.mask), (client_ip_nr & f.mask));
+ */
+ return ( (f.ip & f.mask) == (client_ip_nr & f.mask) );
+}
diff --git a/src/ipfparse.c b/src/ipfparse.c
@@ -0,0 +1,51 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+int ipf_parse (char const *val, IpFilter *res) {
+ char *str, *cp;
+ int counter, nr, i;
+
+ memset (res, 0, sizeof(IpFilter));
+ str = xstrdup (val);
+
+ for (counter = 0, cp = strtok (str, ".");
+ cp;
+ counter += 8, cp = strtok (0, ".")) {
+ if (counter > 24) {
+ warning ("Invalid IP filter specifier '%s' "
+ "(too many network bytes)", val);
+ free (str);
+ return (1);
+ }
+ if (sscanf (cp, "%d", &nr) < 1) {
+ warning ("Invalid IP filter specifier '%s' "
+ "('%s' not a number)", val, cp);
+ free (str);
+ return (2);
+ }
+ nr <<= counter;
+ res->ip |= nr;
+ }
+ free (str);
+
+ if ( (cp = strchr (val, '/')) ) {
+ if (sscanf (cp + 1, "%d", &nr) < 1) {
+ warning ("Invalid IP filter specifier '%s' "
+ "(bad mask '$s')", val, cp + 1);
+ return (3);
+ }
+ for (i = 1; i <= nr; i++) {
+ res->mask <<= 1;
+ res->mask |= 1;
+ }
+ } else
+ res->mask = (unsigned) -1;
+
+ return (0);
+}
+
+
diff --git a/src/ishexdigit.c b/src/ishexdigit.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int is_hex_digit (char ch) {
diff --git a/src/isspace.c b/src/isspace.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
/*
diff --git a/src/lexer.c b/src/lexer.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#line 2 "lexer.c"
/* A lexical scanner generated by flex */
@@ -283,49 +288,55 @@ static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
*yy_cp = '\0'; \
yy_c_buf_p = yy_cp;
-#define YY_NUM_RULES 57
-#define YY_END_OF_BUFFER 58
-static yyconst short int yy_accept[360] =
+#define YY_NUM_RULES 64
+#define YY_END_OF_BUFFER 65
+static yyconst short int yy_accept[404] =
{ 0,
- 0, 0, 0, 0, 0, 0, 58, 46, 44, 45,
- 46, 46, 43, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 55, 56, 54, 55, 55, 53, 50, 49, 50, 44,
- 0, 1, 47, 0, 43, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 41,
- 42, 40, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 0, 51, 0, 52,
- 48, 0, 2, 42, 25, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
-
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 40, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 27, 42, 42, 42, 6, 5, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 24, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 14, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 4, 42, 42, 42, 20,
- 42, 42, 42, 42, 42, 42, 18, 42, 42, 15,
-
- 42, 42, 42, 7, 42, 42, 42, 42, 13, 42,
- 42, 42, 42, 8, 9, 42, 42, 21, 42, 42,
- 42, 42, 42, 42, 42, 42, 3, 42, 42, 42,
- 42, 42, 42, 10, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 42, 32, 31, 42, 42,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 19, 42, 42, 42, 42, 42, 17, 42, 42,
- 42, 26, 42, 29, 42, 42, 42, 42, 42, 42,
-
- 42, 30, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 16, 42, 42, 42, 42, 33,
- 42, 42, 42, 42, 42, 22, 42, 42, 42, 42,
- 42, 28, 42, 42, 42, 42, 42, 12, 42, 42,
- 42, 34, 37, 42, 42, 42, 42, 35, 38, 42,
- 42, 42, 23, 42, 42, 11, 36, 39, 0
+ 0, 0, 0, 0, 0, 0, 65, 53, 51, 52,
+ 53, 53, 50, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 62, 63, 61, 62, 62, 60, 57, 56,
+ 57, 51, 0, 1, 54, 0, 50, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 48, 49, 47, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 0, 58, 0, 59, 55, 0, 2, 49, 49,
+ 27, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 47, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 29, 49, 49, 49, 49, 6,
+ 5, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 26, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 14, 49, 49,
+ 49, 49, 49, 35, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+
+ 49, 49, 49, 49, 49, 49, 4, 49, 49, 49,
+ 20, 49, 49, 49, 49, 49, 49, 49, 34, 49,
+ 18, 49, 49, 15, 49, 49, 49, 7, 49, 49,
+ 49, 49, 49, 13, 49, 49, 49, 49, 49, 49,
+ 8, 9, 49, 49, 21, 49, 49, 49, 49, 49,
+ 49, 49, 33, 49, 49, 3, 49, 49, 49, 49,
+ 49, 49, 49, 10, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 46, 44, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 45, 43, 49, 49, 49, 49, 49, 49,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 19, 49, 49,
+ 49, 49, 49, 49, 17, 49, 49, 49, 28, 49,
+ 31, 49, 49, 49, 49, 49, 49, 49, 49, 32,
+ 49, 49, 49, 49, 49, 49, 49, 24, 49, 49,
+ 49, 49, 49, 49, 16, 49, 49, 49, 49, 49,
+ 36, 49, 49, 49, 49, 49, 22, 49, 49, 49,
+ 49, 49, 49, 30, 49, 49, 49, 49, 49, 49,
+ 12, 49, 49, 49, 37, 40, 49, 49, 49, 23,
+ 49, 38, 41, 49, 49, 49, 25, 49, 49, 11,
+
+ 39, 42, 0
} ;
static yyconst int yy_ec[256] =
@@ -368,194 +379,212 @@ static yyconst int yy_meta[37] =
2, 2, 2, 2, 2, 2
} ;
-static yyconst short int yy_base[367] =
+static yyconst short int yy_base[411] =
{ 0,
- 0, 0, 34, 39, 44, 45, 390, 391, 47, 391,
- 386, 46, 378, 0, 40, 45, 361, 40, 373, 355,
- 371, 357, 44, 356, 46, 53, 40, 364, 363, 362,
- 391, 391, 391, 373, 370, 391, 391, 391, 367, 75,
- 372, 391, 391, 371, 363, 0, 356, 336, 343, 354,
- 343, 66, 342, 351, 336, 340, 340, 332, 327, 0,
- 342, 56, 342, 330, 332, 324, 324, 56, 330, 332,
- 324, 65, 324, 322, 328, 319, 342, 391, 339, 391,
- 391, 342, 391, 72, 0, 327, 321, 326, 315, 309,
- 311, 317, 312, 323, 308, 307, 304, 305, 316, 317,
-
- 298, 300, 297, 310, 304, 299, 291, 73, 300, 306,
- 294, 301, 301, 300, 302, 296, 0, 291, 296, 287,
- 74, 281, 285, 281, 292, 271, 289, 270, 291, 273,
- 285, 0, 275, 279, 284, 0, 0, 272, 265, 280,
- 72, 272, 277, 276, 270, 260, 272, 0, 263, 268,
- 266, 258, 269, 259, 257, 256, 256, 267, 262, 261,
- 262, 0, 246, 247, 249, 250, 257, 247, 249, 241,
- 240, 252, 245, 237, 229, 228, 243, 240, 231, 229,
- 241, 225, 75, 240, 236, 0, 237, 223, 224, 0,
- 221, 235, 236, 223, 216, 229, 0, 220, 218, 0,
-
- 226, 225, 209, 0, 79, 220, 224, 86, 0, 213,
- 220, 213, 218, 0, 0, 219, 212, 0, 211, 211,
- 212, 212, 200, 198, 207, 211, 0, 199, 206, 196,
- 191, 193, 196, 0, 188, 187, 188, 194, 186, 183,
- 186, 185, 186, 191, 193, 190, 177, 184, 183, 173,
- 174, 175, 170, 168, 172, 162, 176, 175, 177, 161,
- 171, 166, 165, 163, 167, 157, 0, 0, 161, 160,
- 164, 163, 160, 154, 150, 160, 161, 160, 151, 158,
- 148, 0, 143, 156, 156, 149, 139, 0, 151, 150,
- 145, 0, 142, 0, 151, 150, 132, 133, 135, 138,
-
- 141, 0, 131, 139, 142, 141, 136, 126, 135, 134,
- 129, 128, 118, 122, 0, 120, 116, 127, 126, 0,
- 122, 123, 122, 121, 120, 0, 119, 106, 102, 116,
- 115, 0, 103, 102, 116, 115, 101, 0, 113, 97,
- 96, 0, 0, 107, 106, 90, 97, 0, 0, 89,
- 88, 70, 0, 70, 36, 0, 0, 0, 391, 107,
- 109, 111, 38, 113, 115, 117
+ 0, 0, 34, 39, 44, 45, 432, 433, 47, 433,
+ 428, 46, 420, 0, 40, 45, 403, 40, 394, 414,
+ 396, 412, 398, 44, 397, 47, 55, 54, 393, 404,
+ 403, 402, 433, 433, 433, 413, 410, 433, 433, 433,
+ 407, 66, 412, 433, 433, 411, 403, 0, 396, 388,
+ 375, 382, 393, 382, 62, 381, 68, 376, 380, 373,
+ 379, 371, 366, 0, 381, 69, 381, 369, 371, 363,
+ 363, 51, 369, 371, 363, 71, 363, 372, 360, 366,
+ 357, 380, 433, 377, 433, 433, 380, 433, 75, 356,
+ 0, 364, 358, 363, 352, 346, 348, 354, 349, 360,
+
+ 337, 344, 343, 352, 339, 340, 351, 340, 351, 333,
+ 334, 331, 344, 338, 333, 325, 77, 334, 340, 328,
+ 335, 335, 334, 322, 335, 329, 0, 324, 329, 312,
+ 319, 77, 313, 317, 313, 324, 303, 321, 302, 318,
+ 322, 304, 305, 315, 0, 305, 314, 308, 315, 0,
+ 0, 301, 294, 309, 78, 301, 306, 305, 299, 289,
+ 301, 0, 305, 291, 296, 294, 286, 295, 296, 286,
+ 284, 283, 283, 294, 289, 288, 289, 0, 75, 273,
+ 274, 276, 275, 0, 276, 270, 273, 275, 267, 266,
+ 278, 271, 263, 255, 254, 269, 266, 271, 256, 254,
+
+ 266, 250, 80, 82, 265, 261, 0, 262, 248, 249,
+ 0, 246, 252, 248, 258, 259, 258, 245, 0, 239,
+ 0, 243, 241, 0, 249, 248, 232, 0, 90, 243,
+ 247, 246, 92, 0, 235, 242, 235, 231, 233, 238,
+ 0, 0, 239, 232, 0, 231, 234, 226, 229, 230,
+ 224, 229, 0, 226, 230, 0, 218, 225, 215, 210,
+ 212, 215, 211, 0, 206, 205, 206, 216, 208, 210,
+ 202, 199, 202, 201, 0, 0, 202, 207, 204, 208,
+ 201, 200, 190, 191, 192, 187, 185, 189, 183, 178,
+ 192, 191, 0, 0, 193, 177, 187, 182, 181, 179,
+
+ 183, 190, 172, 176, 175, 179, 178, 175, 169, 165,
+ 175, 168, 175, 174, 165, 172, 162, 0, 157, 170,
+ 170, 159, 162, 152, 0, 164, 163, 158, 0, 155,
+ 0, 147, 163, 162, 144, 145, 147, 150, 153, 0,
+ 153, 142, 150, 153, 152, 147, 137, 0, 146, 145,
+ 140, 139, 129, 133, 0, 133, 130, 126, 137, 136,
+ 0, 132, 133, 132, 131, 130, 0, 129, 128, 115,
+ 111, 125, 124, 0, 112, 111, 125, 124, 110, 107,
+ 0, 121, 105, 104, 0, 0, 115, 114, 98, 0,
+ 105, 0, 0, 110, 109, 82, 0, 79, 31, 0,
+
+ 0, 0, 433, 113, 115, 117, 38, 119, 121, 123
} ;
-static yyconst short int yy_def[367] =
+static yyconst short int yy_def[411] =
{ 0,
- 359, 1, 360, 360, 361, 361, 359, 359, 359, 359,
- 362, 359, 359, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 359, 359, 359, 364, 365, 359, 359, 359, 359, 359,
- 362, 359, 359, 366, 359, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 364, 359, 365, 359,
- 359, 366, 359, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
-
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
-
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
-
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
- 363, 363, 363, 363, 363, 363, 363, 363, 0, 359,
- 359, 359, 359, 359, 359, 359
+ 403, 1, 404, 404, 405, 405, 403, 403, 403, 403,
+ 406, 403, 403, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 403, 403, 403, 408, 409, 403, 403, 403,
+ 403, 403, 406, 403, 403, 410, 403, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 408, 403, 409, 403, 403, 410, 403, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+
+ 407, 407, 0, 403, 403, 403, 403, 403, 403, 403
} ;
-static yyconst short int yy_nxt[428] =
+static yyconst short int yy_nxt[470] =
{ 0,
8, 9, 10, 9, 8, 11, 8, 8, 12, 13,
- 8, 14, 15, 16, 17, 18, 14, 19, 14, 20,
- 14, 14, 14, 21, 22, 23, 24, 25, 26, 27,
- 14, 28, 29, 14, 30, 14, 32, 33, 34, 46,
- 35, 32, 33, 34, 36, 35, 38, 38, 40, 36,
- 40, 39, 39, 43, 44, 47, 54, 50, 65, 71,
- 55, 61, 66, 358, 48, 51, 49, 72, 62, 68,
- 56, 67, 69, 100, 73, 63, 40, 112, 40, 52,
- 89, 90, 70, 107, 101, 108, 118, 142, 171, 212,
- 154, 91, 172, 230, 92, 113, 155, 357, 231, 356,
-
- 119, 143, 234, 213, 355, 354, 235, 31, 31, 37,
- 37, 41, 41, 77, 77, 79, 79, 82, 82, 353,
- 352, 351, 350, 349, 348, 347, 346, 345, 344, 343,
- 342, 341, 340, 339, 338, 337, 336, 335, 334, 333,
- 332, 331, 330, 329, 328, 327, 326, 325, 324, 323,
- 322, 321, 320, 319, 318, 317, 316, 315, 314, 313,
- 312, 311, 310, 309, 308, 307, 306, 305, 304, 303,
- 302, 301, 300, 299, 298, 297, 296, 295, 294, 293,
- 292, 291, 290, 289, 288, 287, 286, 285, 284, 283,
- 282, 281, 280, 279, 278, 277, 234, 276, 275, 274,
-
- 273, 272, 271, 270, 269, 268, 267, 266, 265, 264,
- 263, 262, 261, 260, 259, 258, 257, 256, 255, 254,
- 253, 252, 251, 250, 249, 248, 247, 246, 245, 244,
- 243, 242, 241, 240, 239, 238, 237, 236, 233, 232,
- 229, 228, 227, 226, 225, 224, 223, 222, 221, 220,
- 219, 218, 217, 216, 215, 214, 211, 210, 209, 208,
- 207, 206, 205, 204, 203, 202, 201, 200, 199, 198,
- 197, 196, 195, 194, 193, 192, 191, 190, 189, 188,
- 187, 186, 185, 184, 183, 182, 181, 180, 179, 178,
- 177, 176, 175, 174, 173, 170, 169, 168, 167, 166,
-
- 165, 60, 164, 163, 162, 161, 160, 159, 158, 157,
- 156, 153, 152, 151, 150, 149, 148, 117, 147, 146,
- 145, 144, 141, 140, 139, 138, 137, 136, 135, 134,
- 133, 132, 131, 130, 129, 128, 127, 126, 125, 124,
- 123, 122, 121, 120, 83, 80, 78, 117, 116, 115,
- 114, 111, 110, 109, 106, 105, 104, 103, 102, 60,
- 99, 98, 97, 96, 95, 94, 93, 88, 87, 86,
- 85, 84, 45, 83, 42, 81, 80, 78, 76, 75,
- 74, 64, 60, 59, 58, 57, 53, 45, 42, 359,
- 7, 359, 359, 359, 359, 359, 359, 359, 359, 359,
-
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359
+ 8, 14, 15, 16, 17, 18, 19, 20, 14, 21,
+ 14, 14, 14, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 14, 32, 14, 34, 35, 36, 48,
+ 37, 34, 35, 36, 38, 37, 40, 40, 42, 38,
+ 42, 41, 41, 45, 46, 49, 57, 53, 402, 69,
+ 58, 65, 50, 70, 51, 54, 52, 42, 66, 42,
+ 59, 72, 71, 75, 73, 67, 95, 96, 116, 55,
+ 117, 76, 100, 121, 74, 108, 109, 97, 77, 128,
+ 98, 156, 101, 170, 190, 213, 239, 110, 191, 171,
+
+ 237, 122, 214, 129, 259, 157, 401, 238, 264, 260,
+ 240, 400, 265, 33, 33, 39, 39, 43, 43, 82,
+ 82, 84, 84, 87, 87, 399, 398, 397, 396, 395,
+ 394, 393, 392, 391, 390, 389, 388, 387, 386, 385,
+ 384, 383, 382, 381, 380, 379, 378, 377, 376, 375,
+ 374, 373, 372, 371, 370, 369, 368, 367, 366, 365,
+ 364, 363, 362, 361, 360, 359, 358, 357, 356, 355,
+ 354, 353, 352, 351, 350, 349, 348, 347, 346, 345,
+ 344, 343, 342, 341, 340, 339, 338, 337, 336, 335,
+ 334, 333, 332, 331, 330, 329, 328, 327, 326, 325,
+
+ 324, 323, 322, 321, 320, 319, 318, 317, 316, 315,
+ 314, 313, 264, 312, 311, 310, 309, 308, 307, 306,
+ 305, 304, 303, 302, 301, 300, 299, 298, 297, 296,
+ 295, 294, 293, 292, 291, 290, 289, 288, 287, 286,
+ 285, 284, 283, 282, 281, 280, 279, 278, 277, 276,
+ 275, 274, 273, 272, 271, 270, 269, 268, 267, 266,
+ 263, 262, 261, 258, 257, 256, 255, 254, 253, 252,
+ 251, 250, 249, 248, 247, 246, 245, 244, 243, 242,
+ 241, 236, 235, 234, 233, 232, 231, 230, 229, 228,
+ 227, 226, 225, 224, 223, 222, 221, 220, 219, 218,
+
+ 217, 216, 215, 212, 211, 210, 209, 208, 207, 206,
+ 205, 204, 203, 202, 201, 200, 199, 198, 197, 196,
+ 195, 194, 193, 192, 189, 188, 187, 186, 185, 184,
+ 183, 64, 182, 181, 180, 179, 178, 177, 176, 175,
+ 174, 173, 172, 169, 168, 167, 166, 165, 164, 163,
+ 162, 127, 161, 160, 159, 158, 155, 154, 153, 152,
+ 151, 150, 149, 148, 147, 146, 145, 144, 143, 142,
+ 141, 140, 139, 138, 137, 136, 135, 134, 133, 132,
+ 131, 130, 88, 85, 83, 127, 126, 125, 124, 123,
+ 120, 119, 118, 115, 114, 113, 112, 111, 64, 107,
+
+ 106, 105, 104, 103, 102, 99, 94, 93, 92, 91,
+ 90, 89, 47, 88, 44, 86, 85, 83, 81, 80,
+ 79, 78, 68, 64, 63, 62, 61, 60, 56, 47,
+ 44, 403, 7, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403
} ;
-static yyconst short int yy_chk[428] =
+static yyconst short int yy_chk[470] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 3, 3, 3, 363,
+ 1, 1, 1, 1, 1, 1, 3, 3, 3, 407,
3, 4, 4, 4, 3, 4, 5, 6, 9, 4,
- 9, 5, 6, 12, 12, 15, 18, 16, 25, 27,
- 18, 23, 25, 355, 15, 16, 15, 27, 23, 26,
- 18, 25, 26, 62, 27, 23, 40, 72, 40, 16,
- 52, 52, 26, 68, 62, 68, 84, 108, 141, 183,
- 121, 52, 141, 205, 52, 72, 121, 354, 205, 352,
-
- 84, 108, 208, 183, 351, 350, 208, 360, 360, 361,
- 361, 362, 362, 364, 364, 365, 365, 366, 366, 347,
- 346, 345, 344, 341, 340, 339, 337, 336, 335, 334,
- 333, 331, 330, 329, 328, 327, 325, 324, 323, 322,
- 321, 319, 318, 317, 316, 314, 313, 312, 311, 310,
- 309, 308, 307, 306, 305, 304, 303, 301, 300, 299,
- 298, 297, 296, 295, 293, 291, 290, 289, 287, 286,
- 285, 284, 283, 281, 280, 279, 278, 277, 276, 275,
- 274, 273, 272, 271, 270, 269, 266, 265, 264, 263,
- 262, 261, 260, 259, 258, 257, 256, 255, 254, 253,
-
- 252, 251, 250, 249, 248, 247, 246, 245, 244, 243,
- 242, 241, 240, 239, 238, 237, 236, 235, 233, 232,
- 231, 230, 229, 228, 226, 225, 224, 223, 222, 221,
- 220, 219, 217, 216, 213, 212, 211, 210, 207, 206,
- 203, 202, 201, 199, 198, 196, 195, 194, 193, 192,
- 191, 189, 188, 187, 185, 184, 182, 181, 180, 179,
- 178, 177, 176, 175, 174, 173, 172, 171, 170, 169,
- 168, 167, 166, 165, 164, 163, 161, 160, 159, 158,
- 157, 156, 155, 154, 153, 152, 151, 150, 149, 147,
- 146, 145, 144, 143, 142, 140, 139, 138, 135, 134,
-
- 133, 131, 130, 129, 128, 127, 126, 125, 124, 123,
- 122, 120, 119, 118, 116, 115, 114, 113, 112, 111,
- 110, 109, 107, 106, 105, 104, 103, 102, 101, 100,
- 99, 98, 97, 96, 95, 94, 93, 92, 91, 90,
- 89, 88, 87, 86, 82, 79, 77, 76, 75, 74,
- 73, 71, 70, 69, 67, 66, 65, 64, 63, 61,
- 59, 58, 57, 56, 55, 54, 53, 51, 50, 49,
- 48, 47, 45, 44, 41, 39, 35, 34, 30, 29,
- 28, 24, 22, 21, 20, 19, 17, 13, 11, 7,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
-
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359
+ 9, 5, 6, 12, 12, 15, 18, 16, 399, 26,
+ 18, 24, 15, 26, 15, 16, 15, 42, 24, 42,
+ 18, 27, 26, 28, 27, 24, 55, 55, 72, 16,
+ 72, 28, 57, 76, 27, 66, 66, 55, 28, 89,
+ 55, 117, 57, 132, 155, 179, 204, 66, 155, 132,
+
+ 203, 76, 179, 89, 229, 117, 398, 203, 233, 229,
+ 204, 396, 233, 404, 404, 405, 405, 406, 406, 408,
+ 408, 409, 409, 410, 410, 395, 394, 391, 389, 388,
+ 387, 384, 383, 382, 380, 379, 378, 377, 376, 375,
+ 373, 372, 371, 370, 369, 368, 366, 365, 364, 363,
+ 362, 360, 359, 358, 357, 356, 354, 353, 352, 351,
+ 350, 349, 347, 346, 345, 344, 343, 342, 341, 339,
+ 338, 337, 336, 335, 334, 333, 332, 330, 328, 327,
+ 326, 324, 323, 322, 321, 320, 319, 317, 316, 315,
+ 314, 313, 312, 311, 310, 309, 308, 307, 306, 305,
+
+ 304, 303, 302, 301, 300, 299, 298, 297, 296, 295,
+ 292, 291, 290, 289, 288, 287, 286, 285, 284, 283,
+ 282, 281, 280, 279, 278, 277, 274, 273, 272, 271,
+ 270, 269, 268, 267, 266, 265, 263, 262, 261, 260,
+ 259, 258, 257, 255, 254, 252, 251, 250, 249, 248,
+ 247, 246, 244, 243, 240, 239, 238, 237, 236, 235,
+ 232, 231, 230, 227, 226, 225, 223, 222, 220, 218,
+ 217, 216, 215, 214, 213, 212, 210, 209, 208, 206,
+ 205, 202, 201, 200, 199, 198, 197, 196, 195, 194,
+ 193, 192, 191, 190, 189, 188, 187, 186, 185, 183,
+
+ 182, 181, 180, 177, 176, 175, 174, 173, 172, 171,
+ 170, 169, 168, 167, 166, 165, 164, 163, 161, 160,
+ 159, 158, 157, 156, 154, 153, 152, 149, 148, 147,
+ 146, 144, 143, 142, 141, 140, 139, 138, 137, 136,
+ 135, 134, 133, 131, 130, 129, 128, 126, 125, 124,
+ 123, 122, 121, 120, 119, 118, 116, 115, 114, 113,
+ 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,
+ 102, 101, 100, 99, 98, 97, 96, 95, 94, 93,
+ 92, 90, 87, 84, 82, 81, 80, 79, 78, 77,
+ 75, 74, 73, 71, 70, 69, 68, 67, 65, 63,
+
+ 62, 61, 60, 59, 58, 56, 54, 53, 52, 51,
+ 50, 49, 47, 46, 43, 41, 37, 36, 32, 31,
+ 30, 29, 25, 23, 22, 21, 20, 19, 17, 13,
+ 11, 7, 403, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
+ 403, 403, 403, 403, 403, 403, 403, 403, 403
} ;
static yy_state_type yy_last_accepting_state;
@@ -596,7 +625,7 @@ static int yywrap () {
#define stringstate 1
#define commentstate 2
-#line 600 "lexer.c"
+#line 624 "lexer.c"
/* Macros after this point can all be overridden by user definitions in
* section 1.
@@ -750,7 +779,7 @@ YY_DECL
#line 27 "lexer.l"
-#line 754 "lexer.c"
+#line 778 "lexer.c"
if ( yy_init )
{
@@ -801,13 +830,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 360 )
+ if ( yy_current_state >= 404 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
++yy_cp;
}
- while ( yy_base[yy_current_state] != 391 );
+ while ( yy_base[yy_current_state] != 433 );
yy_find_action:
yy_act = yy_accept[yy_current_state];
@@ -837,512 +866,583 @@ case 1:
YY_RULE_SETUP
#line 29 "lexer.l"
{
- lmsg ("comment");
- yylineno++;
- }
+ lmsg ("comment");
+ yylineno++;
+}
YY_BREAK
case 2:
YY_RULE_SETUP
-#line 33 "lexer.l"
+#line 34 "lexer.l"
{
- lmsg ("comment");
- yylineno++;
- }
+ lmsg ("comment");
+ yylineno++;
+}
YY_BREAK
case 3:
YY_RULE_SETUP
-#line 37 "lexer.l"
+#line 39 "lexer.l"
{
- lmsg ("service");
- return (SERVICE);
- }
+ lmsg ("service");
+ return (SERVICE);
+}
YY_BREAK
case 4:
YY_RULE_SETUP
-#line 41 "lexer.l"
+#line 44 "lexer.l"
{
- lmsg ("bindto");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (BINDTO);
- }
+ lmsg ("bindto");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (BINDTO);
+}
YY_BREAK
case 5:
YY_RULE_SETUP
-#line 48 "lexer.l"
+#line 52 "lexer.l"
{
- lmsg ("port");
- return (PORT);
- }
+ lmsg ("port");
+ return (PORT);
+}
YY_BREAK
case 6:
YY_RULE_SETUP
-#line 52 "lexer.l"
+#line 57 "lexer.l"
{
- lmsg ("over");
- return (OVER);
- }
+ lmsg ("over");
+ return (OVER);
+}
YY_BREAK
case 7:
YY_RULE_SETUP
-#line 56 "lexer.l"
+#line 62 "lexer.l"
{
- lmsg ("shmkey");
- return (SHMKEY);
- }
+ lmsg ("shmkey");
+ return (SHMKEY);
+}
YY_BREAK
case 8:
YY_RULE_SETUP
-#line 60 "lexer.l"
+#line 67 "lexer.l"
{
- lmsg ("backend");
- return (BACKEND);
- }
+ lmsg ("backend");
+ return (BACKEND);
+}
YY_BREAK
case 9:
YY_RULE_SETUP
-#line 64 "lexer.l"
+#line 72 "lexer.l"
{
- lmsg ("backlog");
- return (BACKLOG);
- }
+ lmsg ("backlog");
+ return (BACKLOG);
+}
YY_BREAK
case 10:
YY_RULE_SETUP
-#line 68 "lexer.l"
+#line 77 "lexer.l"
{
- lmsg ("verbosity");
- return (VERBOSITY);
- }
+ lmsg ("verbosity");
+ return (VERBOSITY);
+}
YY_BREAK
case 11:
YY_RULE_SETUP
-#line 72 "lexer.l"
+#line 82 "lexer.l"
{
- lmsg ("connectiontimeout");
- return (CONNECTIONTIMEOUT);
- }
+ lmsg ("connectiontimeout");
+ return (CONNECTIONTIMEOUT);
+}
YY_BREAK
case 12:
YY_RULE_SETUP
-#line 76 "lexer.l"
+#line 87 "lexer.l"
{
- lmsg ("maxconnections");
- return (MAXCONNECTIONS);
- }
+ lmsg ("maxconnections");
+ return (MAXCONNECTIONS);
+}
YY_BREAK
case 13:
YY_RULE_SETUP
-#line 80 "lexer.l"
+#line 92 "lexer.l"
{
- lmsg ("weight");
- return (WEIGHT);
- }
+ lmsg ("weight");
+ return (WEIGHT);
+}
YY_BREAK
case 14:
YY_RULE_SETUP
-#line 84 "lexer.l"
+#line 97 "lexer.l"
{
- lmsg ("decay");
- return (DECAY);
- }
+ lmsg ("decay");
+ return (DECAY);
+}
YY_BREAK
case 15:
YY_RULE_SETUP
-#line 88 "lexer.l"
+#line 102 "lexer.l"
{
- lmsg ("server");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SERVER);
- }
+ lmsg ("server");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SERVER);
+}
YY_BREAK
case 16:
YY_RULE_SETUP
-#line 95 "lexer.l"
+#line 110 "lexer.l"
{
- lmsg ("dispatchmode");
- return (DISPATCHMODE);
- }
+ lmsg ("dispatchmode");
+ return (DISPATCHMODE);
+}
YY_BREAK
case 17:
YY_RULE_SETUP
-#line 99 "lexer.l"
+#line 115 "lexer.l"
{
- lmsg ("roundrobin");
- return (ROUNDROBIN);
- }
+ lmsg ("roundrobin");
+ return (ROUNDROBIN);
+}
YY_BREAK
case 18:
YY_RULE_SETUP
-#line 103 "lexer.l"
+#line 120 "lexer.l"
{
- lmsg ("random");
- return (RANDOM);
- }
+ lmsg ("random");
+ return (RANDOM);
+}
YY_BREAK
case 19:
YY_RULE_SETUP
-#line 107 "lexer.l"
+#line 125 "lexer.l"
{
- lmsg ("byduration");
- return (BYDURATION);
- }
+ lmsg ("byduration");
+ return (BYDURATION);
+}
YY_BREAK
case 20:
YY_RULE_SETUP
-#line 111 "lexer.l"
+#line 130 "lexer.l"
{
- lmsg ("bysize");
- return (BYSIZE);
- }
+ lmsg ("bysize");
+ return (BYSIZE);
+}
YY_BREAK
case 21:
YY_RULE_SETUP
-#line 115 "lexer.l"
+#line 135 "lexer.l"
{
- lmsg ("byorder");
- return (BYORDER);
- }
+ lmsg ("byorder");
+ return (BYORDER);
+}
YY_BREAK
case 22:
YY_RULE_SETUP
-#line 119 "lexer.l"
+#line 140 "lexer.l"
{
- lmsg ("byconnections");
- return (BYCONNECTIONS);
- }
+ lmsg ("byconnections");
+ return (BYCONNECTIONS);
+}
YY_BREAK
case 23:
YY_RULE_SETUP
-#line 123 "lexer.l"
+#line 145 "lexer.l"
{
- lmsg ("revivinginterval");
- return (REVIVINGINTERVAL);
- }
+ lmsg ("externalhandler");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (EXTERNALHANDLER);
+}
YY_BREAK
case 24:
YY_RULE_SETUP
-#line 127 "lexer.l"
+#line 153 "lexer.l"
{
- lmsg ("type");
- return (TYPE);
- }
+ lmsg ("useraccount");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (USERACCOUNT);
+}
YY_BREAK
case 25:
YY_RULE_SETUP
-#line 131 "lexer.l"
+#line 161 "lexer.l"
{
- lmsg ("any");
- return (ANY);
- }
+ lmsg ("revivinginterval");
+ return (REVIVINGINTERVAL);
+}
YY_BREAK
case 26:
YY_RULE_SETUP
-#line 135 "lexer.l"
+#line 166 "lexer.l"
{
- lmsg ("stickyhttp");
- warning ("The 'stickyhttp protocol "
- "is obsolte.\n"
- "You should change to 'http'.");
- return (HTTP);
- }
+ lmsg ("type");
+ return (TYPE);
+}
YY_BREAK
case 27:
YY_RULE_SETUP
-#line 142 "lexer.l"
+#line 171 "lexer.l"
{
- lmsg ("http");
- return (HTTP);
- }
+ lmsg ("any");
+ return (ANY);
+}
YY_BREAK
case 28:
YY_RULE_SETUP
-#line 146 "lexer.l"
+#line 176 "lexer.l"
{
- lmsg ("throughputlog");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (THROUGHPUTLOG);
- }
+ lmsg ("stickyhttp");
+ warning ("The 'stickyhttp protocol is obsolte.\n"
+ "You should change to 'http'.");
+ return (HTTP);
+}
YY_BREAK
case 29:
YY_RULE_SETUP
-#line 153 "lexer.l"
+#line 183 "lexer.l"
{
- lmsg ("trafficlog");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (TRAFFICLOG);
- }
+ lmsg ("http");
+ return (HTTP);
+}
YY_BREAK
case 30:
YY_RULE_SETUP
-#line 160 "lexer.l"
+#line 188 "lexer.l"
{
- lmsg ("dumptraffic");
- warning ("The 'dumptraffic' statement "
- "is obsolete.\n"
- "You should change to "
- "'trafficlog'.");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (TRAFFICLOG);
- }
+ lmsg ("throughputlog");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (THROUGHPUTLOG);
+}
YY_BREAK
case 31:
YY_RULE_SETUP
-#line 171 "lexer.l"
+#line 196 "lexer.l"
{
- lmsg ("onsuccess");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ONSUCCESS);
- }
+ lmsg ("trafficlog");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (TRAFFICLOG);
+}
YY_BREAK
case 32:
YY_RULE_SETUP
-#line 178 "lexer.l"
+#line 204 "lexer.l"
{
- lmsg ("onfailure");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ONFAILURE);
- }
+ lmsg ("dumptraffic");
+ warning ("The 'dumptraffic' statement is obsolete.\n"
+ "You should change to 'trafficlog'.");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (TRAFFICLOG);
+}
YY_BREAK
case 33:
YY_RULE_SETUP
-#line 185 "lexer.l"
+#line 214 "lexer.l"
{
- lmsg ("stickycookie");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (STICKYCOOKIE);
- }
+ lmsg ("onstart");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONSTART);
+}
YY_BREAK
case 34:
YY_RULE_SETUP
-#line 192 "lexer.l"
+#line 222 "lexer.l"
{
- lmsg ("addclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ADDCLIENTHEADER);
- }
+ lmsg ("onfail");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONFAIL);
+}
YY_BREAK
case 35:
YY_RULE_SETUP
-#line 199 "lexer.l"
+#line 230 "lexer.l"
{
- lmsg ("setclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SETCLIENTHEADER);
- }
+ lmsg ("onend");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONEND);
+}
YY_BREAK
case 36:
YY_RULE_SETUP
-#line 206 "lexer.l"
+#line 238 "lexer.l"
{
- lmsg ("appendclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (APPENDCLIENTHEADER);
- }
+ lmsg ("stickycookie");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (STICKYCOOKIE);
+}
YY_BREAK
case 37:
YY_RULE_SETUP
-#line 213 "lexer.l"
+#line 246 "lexer.l"
{
- lmsg ("addserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ADDSERVERHEADER);
- }
+ lmsg ("addclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ADDCLIENTHEADER);
+}
YY_BREAK
case 38:
YY_RULE_SETUP
-#line 220 "lexer.l"
+#line 254 "lexer.l"
{
- lmsg ("setserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SETSERVERHEADER);
- }
+ lmsg ("setclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SETCLIENTHEADER);
+}
YY_BREAK
case 39:
YY_RULE_SETUP
-#line 227 "lexer.l"
+#line 262 "lexer.l"
{
- lmsg ("appendserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (APPENDSERVERHEADER);
- }
+ lmsg ("appendclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (APPENDCLIENTHEADER);
+}
YY_BREAK
case 40:
YY_RULE_SETUP
-#line 234 "lexer.l"
+#line 270 "lexer.l"
{
- lmsg ("on");
- return (ON);
- }
+ lmsg ("addserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ADDSERVERHEADER);
+}
YY_BREAK
case 41:
YY_RULE_SETUP
-#line 238 "lexer.l"
+#line 278 "lexer.l"
{
- lmsg ("off");
- return (OFF);
- }
+ lmsg ("setserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SETSERVERHEADER);
+}
YY_BREAK
case 42:
YY_RULE_SETUP
-#line 242 "lexer.l"
+#line 286 "lexer.l"
{
- llmsg ("identifier", yytext);
- return (IDENTIFIER);
- }
+ lmsg ("appendserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (APPENDSERVERHEADER);
+}
YY_BREAK
case 43:
YY_RULE_SETUP
-#line 246 "lexer.l"
+#line 294 "lexer.l"
{
- llmsg ("number", yytext);
- return (NUMBER);
- }
+ lmsg ("allowfrom");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ALLOWFROM);
+}
YY_BREAK
case 44:
YY_RULE_SETUP
-#line 250 "lexer.l"
+#line 302 "lexer.l"
{
- lmsg ("space(s)");
- }
+ lmsg ("denyfrom");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (DENYFROM);
+}
YY_BREAK
case 45:
YY_RULE_SETUP
-#line 253 "lexer.l"
+#line 310 "lexer.l"
{
- lmsg ("newline");
- yylineno++;
- }
+ lmsg ("allowfile");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ALLOWFILE);
+}
YY_BREAK
case 46:
YY_RULE_SETUP
-#line 257 "lexer.l"
+#line 318 "lexer.l"
{
- llmsg ("lone char", yytext);
- return (*yytext);
- }
+ lmsg ("denyfile");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (DENYFILE);
+}
YY_BREAK
case 47:
YY_RULE_SETUP
-#line 261 "lexer.l"
+#line 326 "lexer.l"
{
- lmsg ("C-comment starts");
- BEGIN(commentstate);
- }
+ lmsg ("on");
+ return (ON);
+}
YY_BREAK
case 48:
YY_RULE_SETUP
-#line 265 "lexer.l"
+#line 331 "lexer.l"
{
- lmsg ("C-comment ends");
- BEGIN(0);
- }
+ lmsg ("off");
+ return (OFF);
+}
YY_BREAK
case 49:
YY_RULE_SETUP
-#line 269 "lexer.l"
+#line 336 "lexer.l"
{
- yylineno++;
- }
+ llmsg ("identifier", yytext);
+ return (IDENTIFIER);
+}
YY_BREAK
case 50:
YY_RULE_SETUP
-#line 272 "lexer.l"
-;
+#line 341 "lexer.l"
+{
+ llmsg ("number", yytext);
+ return (NUMBER);
+}
YY_BREAK
case 51:
YY_RULE_SETUP
-#line 274 "lexer.l"
+#line 346 "lexer.l"
{
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext + 1);
- laststring[strlen(laststring) - 1] = 0;
- }
+ lmsg ("space(s)");
+}
YY_BREAK
case 52:
YY_RULE_SETUP
-#line 280 "lexer.l"
+#line 350 "lexer.l"
{
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext + 1);
- laststring[strlen(laststring) - 1] = 0;
- }
+ lmsg ("newline");
+ yylineno++;
+}
YY_BREAK
case 53:
YY_RULE_SETUP
-#line 286 "lexer.l"
+#line 355 "lexer.l"
{
- BEGIN (0);
- unput (';');
- llmsg ("string", laststring);
- return (STRING);
- }
+ llmsg ("lone char", yytext);
+ return (*yytext);
+}
YY_BREAK
case 54:
YY_RULE_SETUP
-#line 292 "lexer.l"
+#line 360 "lexer.l"
{
- if (laststring) {
- laststring = xstrcat (laststring,
- yytext);
- llmsg ("string part", yytext);
- }
- }
+ lmsg ("C-comment starts");
+ BEGIN(commentstate);
+}
YY_BREAK
case 55:
YY_RULE_SETUP
-#line 299 "lexer.l"
+#line 364 "lexer.l"
{
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext);
- }
+ lmsg ("C-comment ends");
+ BEGIN(0);
+}
YY_BREAK
case 56:
YY_RULE_SETUP
-#line 304 "lexer.l"
+#line 368 "lexer.l"
{
- lmsg ("string part: ERROR - newline");
- error ("Parse eror at line %d: "
- "unterminated string, ';' "
- "expected\n", yylineno + 1);
- }
+ yylineno++;
+}
YY_BREAK
case 57:
YY_RULE_SETUP
-#line 310 "lexer.l"
+#line 371 "lexer.l"
+;
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 373 "lexer.l"
+{
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext + 1);
+ laststring[strlen(laststring) - 1] = 0;
+}
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 378 "lexer.l"
+{
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext + 1);
+ laststring[strlen(laststring) - 1] = 0;
+}
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 383 "lexer.l"
+{
+ BEGIN (0);
+ unput (';');
+ llmsg ("string", laststring);
+ return (STRING);
+}
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 389 "lexer.l"
+{
+ if (laststring) {
+ laststring = xstrcat (laststring, yytext);
+ llmsg ("string part", yytext);
+ }
+}
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 395 "lexer.l"
+{
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext);
+}
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 399 "lexer.l"
+{
+ if (laststring) {
+ laststring = xstrcat (laststring, " ");
+ lmsg ("string part: newline, now space");
+ }
+ yylineno++;
+}
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 406 "lexer.l"
ECHO;
YY_BREAK
-#line 1346 "lexer.c"
+#line 1441 "lexer.c"
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(stringstate):
case YY_STATE_EOF(commentstate):
@@ -1636,7 +1736,7 @@ static yy_state_type yy_get_previous_state()
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 360 )
+ if ( yy_current_state >= 404 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1671,11 +1771,11 @@ yy_state_type yy_current_state;
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 360 )
+ if ( yy_current_state >= 404 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- yy_is_jam = (yy_current_state == 359);
+ yy_is_jam = (yy_current_state == 403);
return yy_is_jam ? 0 : yy_current_state;
}
@@ -2230,4 +2330,4 @@ int main()
return 0;
}
#endif
-#line 310 "lexer.l"
+#line 406 "lexer.l"
diff --git a/src/lexer.l b/src/lexer.l
@@ -26,284 +26,380 @@ static int yywrap () {
%%
-#.*\n {
- lmsg ("comment");
- yylineno++;
- }
-\/\/.*\n {
- lmsg ("comment");
- yylineno++;
- }
-service {
- lmsg ("service");
- return (SERVICE);
- }
-bindto {
- lmsg ("bindto");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (BINDTO);
- }
-port {
- lmsg ("port");
- return (PORT);
- }
-over {
- lmsg ("over");
- return (OVER);
- }
-shmkey {
- lmsg ("shmkey");
- return (SHMKEY);
- }
-backend {
- lmsg ("backend");
- return (BACKEND);
- }
-backlog {
- lmsg ("backlog");
- return (BACKLOG);
- }
-(verbosity)|(verbose) {
- lmsg ("verbosity");
- return (VERBOSITY);
- }
-connectiontimeout {
- lmsg ("connectiontimeout");
- return (CONNECTIONTIMEOUT);
- }
-maxconnections {
- lmsg ("maxconnections");
- return (MAXCONNECTIONS);
- }
-weight {
- lmsg ("weight");
- return (WEIGHT);
- }
-decay {
- lmsg ("decay");
- return (DECAY);
- }
-server {
- lmsg ("server");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SERVER);
- }
-dispatchmode {
- lmsg ("dispatchmode");
- return (DISPATCHMODE);
- }
-roundrobin {
- lmsg ("roundrobin");
- return (ROUNDROBIN);
- }
-random {
- lmsg ("random");
- return (RANDOM);
- }
-byduration {
- lmsg ("byduration");
- return (BYDURATION);
- }
-bysize {
- lmsg ("bysize");
- return (BYSIZE);
- }
-byorder {
- lmsg ("byorder");
- return (BYORDER);
- }
-byconnections {
- lmsg ("byconnections");
- return (BYCONNECTIONS);
- }
-revivinginterval {
- lmsg ("revivinginterval");
- return (REVIVINGINTERVAL);
- }
-type {
- lmsg ("type");
- return (TYPE);
- }
-any {
- lmsg ("any");
- return (ANY);
- }
-stickyhttp {
- lmsg ("stickyhttp");
- warning ("The 'stickyhttp protocol "
- "is obsolte.\n"
- "You should change to 'http'.");
- return (HTTP);
- }
-http {
- lmsg ("http");
- return (HTTP);
- }
-throughputlog {
- lmsg ("throughputlog");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (THROUGHPUTLOG);
- }
-trafficlog {
- lmsg ("trafficlog");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (TRAFFICLOG);
- }
-dumptraffic {
- lmsg ("dumptraffic");
- warning ("The 'dumptraffic' statement "
- "is obsolete.\n"
- "You should change to "
- "'trafficlog'.");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (TRAFFICLOG);
- }
-onsuccess {
- lmsg ("onsuccess");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ONSUCCESS);
- }
-onfailure {
- lmsg ("onfailure");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ONFAILURE);
- }
-stickycookie {
- lmsg ("stickycookie");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (STICKYCOOKIE);
- }
-addclientheader {
- lmsg ("addclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ADDCLIENTHEADER);
- }
-setclientheader {
- lmsg ("setclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SETCLIENTHEADER);
- }
-appendclientheader {
- lmsg ("appendclientheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (APPENDCLIENTHEADER);
- }
-addserverheader {
- lmsg ("addserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (ADDSERVERHEADER);
- }
-setserverheader {
- lmsg ("setserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (SETSERVERHEADER);
- }
-appendserverheader {
- lmsg ("appendserverheader");
- BEGIN (stringstate);
- free (laststring);
- laststring = 0;
- return (APPENDSERVERHEADER);
- }
-on|true|yes {
- lmsg ("on");
- return (ON);
- }
-off|false|no {
- lmsg ("off");
- return (OFF);
- }
-[a-zA-Z_][a-zA-Z0-9_]* {
- llmsg ("identifier", yytext);
- return (IDENTIFIER);
- }
-[0-9]+ {
- llmsg ("number", yytext);
- return (NUMBER);
- }
-[ \t]+ {
- lmsg ("space(s)");
- }
-\n {
- lmsg ("newline");
- yylineno++;
- }
-. {
- llmsg ("lone char", yytext);
- return (*yytext);
- }
-\/\* {
- lmsg ("C-comment starts");
- BEGIN(commentstate);
- }
-<commentstate>\*\/ {
- lmsg ("C-comment ends");
- BEGIN(0);
- }
-<commentstate>\n {
- yylineno++;
- }
-<commentstate>. ;
-
-<stringstate>\"[^\"]*\" {
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext + 1);
- laststring[strlen(laststring) - 1] = 0;
- }
-<stringstate>\'[^\']*\' {
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext + 1);
- laststring[strlen(laststring) - 1] = 0;
- }
-<stringstate>; {
- BEGIN (0);
- unput (';');
- llmsg ("string", laststring);
- return (STRING);
- }
-<stringstate>" " {
- if (laststring) {
- laststring = xstrcat (laststring,
- yytext);
- llmsg ("string part", yytext);
- }
- }
-<stringstate>. {
- llmsg ("string part", yytext);
- laststring = xstrcat (laststring,
- yytext);
- }
-<stringstate>\n {
- lmsg ("string part: ERROR - newline");
- error ("Parse eror at line %d: "
- "unterminated string, ';' "
- "expected\n", yylineno + 1);
- }
+#.*\n {
+ lmsg ("comment");
+ yylineno++;
+}
+
+\/\/.*\n {
+ lmsg ("comment");
+ yylineno++;
+}
+
+service {
+ lmsg ("service");
+ return (SERVICE);
+}
+
+bindto {
+ lmsg ("bindto");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (BINDTO);
+}
+
+port {
+ lmsg ("port");
+ return (PORT);
+}
+
+over {
+ lmsg ("over");
+ return (OVER);
+}
+
+shmkey {
+ lmsg ("shmkey");
+ return (SHMKEY);
+}
+
+backend {
+ lmsg ("backend");
+ return (BACKEND);
+}
+
+backlog {
+ lmsg ("backlog");
+ return (BACKLOG);
+}
+
+(verbosity)|(verbose) {
+ lmsg ("verbosity");
+ return (VERBOSITY);
+}
+
+connectiontimeout {
+ lmsg ("connectiontimeout");
+ return (CONNECTIONTIMEOUT);
+}
+
+maxconnections {
+ lmsg ("maxconnections");
+ return (MAXCONNECTIONS);
+}
+
+weight {
+ lmsg ("weight");
+ return (WEIGHT);
+}
+
+decay {
+ lmsg ("decay");
+ return (DECAY);
+}
+
+server {
+ lmsg ("server");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SERVER);
+}
+
+dispatchmode {
+ lmsg ("dispatchmode");
+ return (DISPATCHMODE);
+}
+
+roundrobin {
+ lmsg ("roundrobin");
+ return (ROUNDROBIN);
+}
+
+random {
+ lmsg ("random");
+ return (RANDOM);
+}
+
+byduration {
+ lmsg ("byduration");
+ return (BYDURATION);
+}
+
+bysize {
+ lmsg ("bysize");
+ return (BYSIZE);
+}
+
+byorder {
+ lmsg ("byorder");
+ return (BYORDER);
+}
+
+byconnections {
+ lmsg ("byconnections");
+ return (BYCONNECTIONS);
+}
+
+externalhandler {
+ lmsg ("externalhandler");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (EXTERNALHANDLER);
+}
+
+useraccount {
+ lmsg ("useraccount");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (USERACCOUNT);
+}
+
+revivinginterval {
+ lmsg ("revivinginterval");
+ return (REVIVINGINTERVAL);
+}
+
+type {
+ lmsg ("type");
+ return (TYPE);
+}
+
+any {
+ lmsg ("any");
+ return (ANY);
+}
+
+stickyhttp {
+ lmsg ("stickyhttp");
+ warning ("The 'stickyhttp protocol is obsolte.\n"
+ "You should change to 'http'.");
+ return (HTTP);
+}
+
+http {
+ lmsg ("http");
+ return (HTTP);
+}
+
+throughputlog {
+ lmsg ("throughputlog");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (THROUGHPUTLOG);
+}
+
+trafficlog {
+ lmsg ("trafficlog");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (TRAFFICLOG);
+}
+
+dumptraffic {
+ lmsg ("dumptraffic");
+ warning ("The 'dumptraffic' statement is obsolete.\n"
+ "You should change to 'trafficlog'.");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (TRAFFICLOG);
+}
+
+onstart {
+ lmsg ("onstart");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONSTART);
+}
+
+onfail {
+ lmsg ("onfail");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONFAIL);
+}
+
+onend {
+ lmsg ("onend");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ONEND);
+}
+
+stickycookie {
+ lmsg ("stickycookie");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (STICKYCOOKIE);
+}
+
+addclientheader {
+ lmsg ("addclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ADDCLIENTHEADER);
+}
+
+setclientheader {
+ lmsg ("setclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SETCLIENTHEADER);
+}
+
+appendclientheader {
+ lmsg ("appendclientheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (APPENDCLIENTHEADER);
+}
+
+addserverheader {
+ lmsg ("addserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ADDSERVERHEADER);
+}
+
+setserverheader {
+ lmsg ("setserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (SETSERVERHEADER);
+}
+
+appendserverheader {
+ lmsg ("appendserverheader");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (APPENDSERVERHEADER);
+}
+
+allowfrom {
+ lmsg ("allowfrom");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ALLOWFROM);
+}
+
+denyfrom {
+ lmsg ("denyfrom");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (DENYFROM);
+}
+
+allowfile {
+ lmsg ("allowfile");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (ALLOWFILE);
+}
+
+denyfile {
+ lmsg ("denyfile");
+ BEGIN (stringstate);
+ free (laststring);
+ laststring = 0;
+ return (DENYFILE);
+}
+
+on|true|yes {
+ lmsg ("on");
+ return (ON);
+}
+
+off|false|no {
+ lmsg ("off");
+ return (OFF);
+}
+
+[a-zA-Z_][a-zA-Z0-9_]* {
+ llmsg ("identifier", yytext);
+ return (IDENTIFIER);
+}
+
+[0-9]+ {
+ llmsg ("number", yytext);
+ return (NUMBER);
+}
+
+[ \t]+ {
+ lmsg ("space(s)");
+}
+
+\n {
+ lmsg ("newline");
+ yylineno++;
+}
+
+. {
+ llmsg ("lone char", yytext);
+ return (*yytext);
+}
+
+\/\* {
+ lmsg ("C-comment starts");
+ BEGIN(commentstate);
+}
+<commentstate>\*\/ {
+ lmsg ("C-comment ends");
+ BEGIN(0);
+}
+<commentstate>\n {
+ yylineno++;
+}
+<commentstate>. ;
+
+<stringstate>\"[^\"]*\" {
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext + 1);
+ laststring[strlen(laststring) - 1] = 0;
+}
+<stringstate>\'[^\']*\' {
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext + 1);
+ laststring[strlen(laststring) - 1] = 0;
+}
+<stringstate>; {
+ BEGIN (0);
+ unput (';');
+ llmsg ("string", laststring);
+ return (STRING);
+}
+<stringstate>" " {
+ if (laststring) {
+ laststring = xstrcat (laststring, yytext);
+ llmsg ("string part", yytext);
+ }
+}
+<stringstate>. {
+ llmsg ("string part", yytext);
+ laststring = xstrcat (laststring, yytext);
+}
+<stringstate>\n {
+ if (laststring) {
+ laststring = xstrcat (laststring, " ");
+ lmsg ("string part: newline, now space");
+ }
+ yylineno++;
+}
diff --git a/src/lockreporter.c b/src/lockreporter.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void lock_reporter() {
diff --git a/src/logactivityany.c b/src/logactivityany.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void log_activity_any (char const *action) {
diff --git a/src/logactivitycontinuation.c b/src/logactivitycontinuation.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void log_activity_continuation () {
diff --git a/src/logactivityend.c b/src/logactivityend.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void log_activity_end () {
diff --git a/src/logactivitystart.c b/src/logactivitystart.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void log_activity_start () {
diff --git a/src/main.c b/src/main.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#define EXTERN
#include "crossroads.h"
@@ -19,6 +24,9 @@ char *state_to_string_map [] = { /* backend states as strings */
int relevant_sigs[] = { /* signals relevant to this app */
SIGHUP, /* either caught or ignored, */
SIGINT, /* depending on the stage */
+ SIGQUIT,
+ SIGABRT,
+ SIGKILL,
SIGPIPE,
SIGTERM,
0,
@@ -35,7 +43,6 @@ int main (int argc, char **argv) {
int opt, i;
static Handler handler[] = {
// Argument Nr.args Needconf Handler function
- { "sampleconf", 0, 0, sample_conf },
{ "services", 0, 1, show_services },
{ "status", 0, 1, show_status },
{ "stop", 0, 1, stop_daemon },
diff --git a/src/makesocket.c b/src/makesocket.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
diff --git a/src/markactivity.c b/src/markactivity.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void mark_activity (unsigned long long nbytes, double nsec,
@@ -90,10 +95,7 @@ void mark_activity (unsigned long long nbytes, double nsec,
state_to_string
(servicereport->backendstate[current_backend].avail));
- /* At the end of a request, check the onsuccess/onfailure handlers.
- * We only do this for the 'final' states. */
- if (newstate == st_available)
- sysrun (activeservice->backend[current_backend].onsuccess);
- else if (newstate == st_unavailable)
- sysrun (activeservice->backend[current_backend].onfailure);
+ /* Run the onfailure hook if one is specified. */
+ if (newstate == st_unavailable)
+ sysrun (activeservice->backend[current_backend].onfail);
}
diff --git a/src/msg.c b/src/msg.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void msg (char const *fmt, ...) {
diff --git a/src/msgdumpbuf.c b/src/msgdumpbuf.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void msgdumpbuf (unsigned char const *buf, int buflen) {
diff --git a/src/netbuffer.c b/src/netbuffer.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
unsigned char *net_buffer (CopyDirection dir, unsigned *sz) {
diff --git a/src/netbufread.c b/src/netbufread.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
unsigned char *net_bufread (int sock, unsigned max, unsigned *nread,
diff --git a/src/netcopy.c b/src/netcopy.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
unsigned net_copy (int cl, int sr, unsigned max, unsigned char *buf) {
@@ -57,6 +62,8 @@ unsigned net_copy (int cl, int sr, unsigned max, unsigned char *buf) {
nread = read (dir == dir_client_to_server ? cl : sr, buf, max);
if (nread < 1) {
+ decr_client_count();
+ log_activity_end();
if (nread < 0) {
if (dir == dir_server_to_client)
mark_activity (0, 0, st_unavailable);
diff --git a/src/netread.c b/src/netread.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
unsigned net_read (int sock, unsigned max, unsigned char *buf, int is_client) {
@@ -51,6 +56,8 @@ unsigned net_read (int sock, unsigned max, unsigned char *buf, int is_client) {
/* Do the read. */
nread = read (sock, buf, max - 1);
if (nread < 1) {
+ decr_client_count();
+ log_activity_end();
if (nread < 0) {
mark_activity (0, 0, is_client ? st_available : st_unavailable);
error ("Service %s: read error when getting data from %s",
diff --git a/src/netwrite.c b/src/netwrite.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
/* Define DUMPFILE if you want Crossroads to dump the contents of what's
diff --git a/src/parser.c b/src/parser.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
/* A Bison parser, made from parser.y
by GNU bison 1.35. */
@@ -17,8 +22,8 @@
# define REVIVINGINTERVAL 268
# define SHMKEY 269
# define WEIGHT 270
-# define ONSUCCESS 271
-# define ONFAILURE 272
+# define ONSTART 271
+# define ONFAIL 272
# define STRING 273
# define BACKLOG 274
# define RANDOM 275
@@ -47,6 +52,9 @@
# define DENYFROM 298
# define ALLOWFILE 299
# define DENYFILE 300
+# define EXTERNALHANDLER 301
+# define ONEND 302
+# define USERACCOUNT 303
#line 3 "parser.y"
@@ -55,8 +63,13 @@
#define YYSTYPE Confsets
+/* Static parser vars */
+static int i; /* Loop counter */
+static Backend cur_backend; /* Storage for a handled backend */
+static Service cur_service; /* Storage for a handled service */
+
/* Parser debugging related */
-// #define PARSER_DEBUG
+// #define PARSER_DEBUG
#ifdef PARSER_DEBUG
static void pmsg (char const *x) {
printf ("P: %s\n", x);
@@ -73,13 +86,13 @@
# define pimsg(x,y)
#endif
-/* Error handler for yyparse() */
+/* Error handler for yyparse() */
static int yyerror (char *msg) {
error ("Parse error at line %d, '%s': %s",
yylineno + 1, yytext, yyerrmsg);
}
-/* Store an encountered string */
+/* Store an encountered string */
static char *laststr;
static void setlaststr (char const *what) {
free (laststr);
@@ -98,6 +111,13 @@ static void setlastovernr (char const *what) {
lastovernr = atoi(what);
}
+/* Store an encountered 'externalhandler' */
+static char *lastext;
+static void setlastext (char const *what) {
+ free (lastext);
+ lastext = xstrdup (what);
+}
+
/* Get the server part from HOSTNAME:PORT in allocated memory */
static char *serverpart (char const *what) {
char *ret = xstrdup (what);
@@ -117,10 +137,37 @@ static int portpart (char const *what) {
return (0);
}
-/* Temp vars */
-static int i; /* Loop counter */
-static Backend cur_backend; /* Storage for a handled backend */
-static Service cur_service; /* Storage for a handled service */
+/* Add a list of IP filters to the allowlist or the denylist */
+static void add_any (char *what, int chain) {
+ char *item;
+ int result;
+ for (item = strtok (what, " "); item; item = strtok (0, " ")) {
+ if (chain)
+ result = ipf_add_allow (&cur_service, item);
+ else
+ result = ipf_add_deny (&cur_service, item);
+ if (result)
+ error ("Bad IP filter specifier '%s' at line %d",
+ item, yylineno + 1);
+ }
+}
+static void add_allowfrom (char *what) {
+ add_any (what, 1);
+}
+static void add_denyfrom (char *what) {
+ add_any (what, 0);
+}
+
+/* Set uid/gid for external commands. */
+static void setuseraccount (char *username) {
+ struct passwd *pw;
+
+ if (! (pw = getpwnam (username)) )
+ error ("Invalid username '%s' at line %d", username, yylineno + 1);
+ cur_service.uid = pw->pw_uid;
+ cur_service.gid = pw->pw_gid;
+}
+
#ifndef YYSTYPE
# define YYSTYPE int
# define YYSTYPE_IS_TRIVIAL 1
@@ -131,12 +178,12 @@ static Service cur_service; /* Storage for a handled service */
-#define YYFINAL 165
+#define YYFINAL 197
#define YYFLAG -32768
-#define YYNTBASE 50
+#define YYNTBASE 53
/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */
-#define YYTRANSLATE(x) ((unsigned)(x) <= 300 ? yytranslate[x] : 117)
+#define YYTRANSLATE(x) ((unsigned)(x) <= 303 ? yytranslate[x] : 132)
/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */
static const char yytranslate[] =
@@ -146,14 +193,14 @@ static const char yytranslate[] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 49,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 52,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 47, 2, 48, 2, 2, 2, 2,
+ 2, 2, 2, 50, 2, 51, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -171,7 +218,7 @@ static const char yytranslate[] =
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
- 46
+ 46, 47, 48, 49
};
#if YYDEBUG
@@ -179,45 +226,53 @@ static const short yyprhs[] =
{
0, 0, 3, 5, 11, 14, 17, 20, 22, 25,
27, 29, 31, 33, 35, 37, 39, 41, 43, 45,
- 47, 51, 55, 58, 61, 64, 69, 71, 73, 78,
- 81, 82, 85, 88, 90, 92, 94, 96, 98, 100,
- 104, 108, 112, 116, 120, 124, 127, 129, 131, 137,
- 140, 143, 145, 148, 150, 152, 154, 156, 158, 160,
- 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
- 182, 187, 191, 195, 197, 201, 205, 209, 213, 216,
- 219, 223, 226, 230, 234, 238, 242, 246, 250, 253,
- 254, 255, 256, 257, 258, 259, 260, 261, 262, 263,
- 264, 265, 266, 267, 268
+ 47, 49, 51, 53, 55, 57, 61, 65, 68, 71,
+ 74, 79, 81, 83, 88, 90, 92, 93, 96, 99,
+ 101, 104, 106, 108, 110, 112, 114, 116, 118, 122,
+ 125, 129, 133, 137, 141, 145, 149, 152, 154, 156,
+ 160, 164, 168, 172, 175, 181, 184, 187, 189, 192,
+ 194, 196, 198, 200, 202, 204, 206, 208, 210, 212,
+ 214, 216, 218, 220, 222, 224, 226, 228, 233, 237,
+ 241, 243, 247, 251, 255, 259, 263, 266, 269, 273,
+ 276, 280, 284, 288, 292, 296, 300, 303, 304, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 318, 319, 320
};
static const short yyrhs[] =
{
- 51, 50, 0, 51, 0, 52, 53, 47, 54, 48,
- 0, 105, 3, 0, 113, 4, 0, 54, 55, 0,
- 55, 0, 107, 56, 0, 57, 0, 58, 0, 62,
- 0, 64, 0, 69, 0, 70, 0, 71, 0, 72,
- 0, 73, 0, 74, 0, 77, 0, 5, 60, 61,
- 0, 31, 59, 61, 0, 115, 19, 0, 103, 6,
- 0, 108, 49, 0, 8, 109, 63, 61, 0, 10,
- 0, 11, 0, 12, 67, 65, 61, 0, 29, 66,
- 0, 0, 103, 6, 0, 110, 68, 0, 13, 0,
- 21, 0, 22, 0, 23, 0, 27, 0, 24, 0,
- 14, 60, 61, 0, 20, 60, 61, 0, 15, 60,
- 61, 0, 25, 60, 61, 0, 26, 60, 61, 0,
- 33, 75, 61, 0, 116, 76, 0, 34, 0, 35,
- 0, 7, 78, 47, 79, 48, 0, 114, 4, 0,
- 79, 80, 0, 80, 0, 106, 81, 0, 82, 0,
- 57, 0, 62, 0, 86, 0, 87, 0, 88, 0,
- 89, 0, 83, 0, 84, 0, 73, 0, 92, 0,
- 94, 0, 95, 0, 96, 0, 97, 0, 98, 0,
- 99, 0, 9, 104, 85, 61, 0, 16, 60, 61,
- 0, 30, 60, 61, 0, 19, 0, 17, 90, 61,
- 0, 18, 90, 61, 0, 28, 91, 61, 0, 32,
- 91, 61, 0, 111, 19, 0, 112, 19, 0, 36,
- 93, 61, 0, 102, 19, 0, 37, 100, 61, 0,
- 38, 100, 61, 0, 39, 100, 61, 0, 40, 100,
- 61, 0, 41, 100, 61, 0, 42, 100, 61, 0,
- 101, 19, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 54, 53, 0, 54, 0, 55, 56, 50, 57, 51,
+ 0, 118, 3, 0, 126, 4, 0, 57, 58, 0,
+ 58, 0, 120, 59, 0, 60, 0, 61, 0, 65,
+ 0, 67, 0, 76, 0, 77, 0, 78, 0, 79,
+ 0, 80, 0, 81, 0, 84, 0, 86, 0, 85,
+ 0, 87, 0, 74, 0, 89, 0, 5, 63, 64,
+ 0, 31, 62, 64, 0, 128, 19, 0, 116, 6,
+ 0, 121, 52, 0, 8, 122, 66, 64, 0, 10,
+ 0, 11, 0, 12, 72, 68, 64, 0, 69, 0,
+ 71, 0, 0, 29, 70, 0, 116, 6, 0, 103,
+ 0, 123, 73, 0, 13, 0, 21, 0, 22, 0,
+ 23, 0, 27, 0, 24, 0, 47, 0, 49, 75,
+ 64, 0, 131, 19, 0, 14, 63, 64, 0, 20,
+ 63, 64, 0, 15, 63, 64, 0, 25, 63, 64,
+ 0, 26, 63, 64, 0, 33, 82, 64, 0, 129,
+ 83, 0, 34, 0, 35, 0, 43, 88, 64, 0,
+ 44, 88, 64, 0, 45, 104, 64, 0, 46, 104,
+ 64, 0, 130, 19, 0, 7, 90, 50, 91, 51,
+ 0, 127, 4, 0, 91, 92, 0, 92, 0, 119,
+ 93, 0, 94, 0, 60, 0, 65, 0, 98, 0,
+ 100, 0, 99, 0, 101, 0, 102, 0, 95, 0,
+ 96, 0, 80, 0, 105, 0, 107, 0, 108, 0,
+ 109, 0, 110, 0, 111, 0, 112, 0, 9, 117,
+ 97, 64, 0, 16, 63, 64, 0, 30, 63, 64,
+ 0, 19, 0, 17, 103, 64, 0, 18, 103, 64,
+ 0, 48, 103, 64, 0, 28, 104, 64, 0, 32,
+ 104, 64, 0, 124, 19, 0, 125, 19, 0, 36,
+ 106, 64, 0, 115, 19, 0, 37, 113, 64, 0,
+ 38, 113, 64, 0, 39, 113, 64, 0, 40, 113,
+ 64, 0, 41, 113, 64, 0, 42, 113, 64, 0,
+ 114, 19, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
};
#endif
@@ -226,17 +281,19 @@ static const short yyrhs[] =
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const short yyrline[] =
{
- 0, 90, 93, 97, 126, 131, 139, 142, 146, 151,
- 157, 163, 169, 177, 183, 189, 195, 201, 207, 228,
- 377, 389, 401, 410, 417, 422, 435, 439, 445, 464,
- 467, 473, 480, 485, 489, 493, 497, 501, 505, 511,
- 523, 535, 547, 559, 571, 583, 588, 592, 598, 608,
- 616, 624, 630, 637, 642, 647, 652, 657, 662, 667,
- 672, 677, 682, 687, 692, 697, 702, 707, 712, 717,
- 724, 737, 749, 761, 767, 779, 791, 803, 815, 824,
- 833, 845, 854, 866, 878, 890, 902, 914, 926, 935,
- 940, 945, 950, 955, 960, 965, 970, 975, 980, 985,
- 990, 995, 1000, 1005, 1010
+ 0, 130, 133, 137, 166, 171, 179, 182, 186, 191,
+ 197, 203, 209, 219, 225, 231, 237, 243, 249, 255,
+ 261, 267, 273, 279, 286, 439, 451, 463, 472, 479,
+ 484, 497, 501, 507, 516, 518, 520, 524, 541, 548,
+ 558, 567, 571, 575, 579, 583, 587, 591, 597, 609,
+ 618, 630, 642, 654, 666, 678, 690, 695, 699, 705,
+ 717, 729, 741, 753, 762, 772, 780, 788, 794, 801,
+ 806, 811, 816, 821, 826, 831, 836, 841, 846, 851,
+ 856, 861, 866, 871, 876, 881, 886, 893, 906, 918,
+ 930, 936, 948, 960, 972, 984, 996, 1005, 1014, 1026,
+ 1035, 1047, 1059, 1071, 1083, 1095, 1107, 1116, 1121, 1126,
+ 1131, 1136, 1141, 1146, 1151, 1156, 1161, 1166, 1171, 1176,
+ 1181, 1186, 1191, 1196, 1201
};
#endif
@@ -248,51 +305,58 @@ static const char *const yytname[] =
{
"$", "error", "$undefined.", "SERVICE", "IDENTIFIER", "PORT", "NUMBER",
"BACKEND", "VERBOSITY", "SERVER", "ON", "OFF", "DISPATCHMODE",
- "ROUNDROBIN", "REVIVINGINTERVAL", "SHMKEY", "WEIGHT", "ONSUCCESS",
- "ONFAILURE", "STRING", "BACKLOG", "RANDOM", "BYDURATION", "BYSIZE",
+ "ROUNDROBIN", "REVIVINGINTERVAL", "SHMKEY", "WEIGHT", "ONSTART",
+ "ONFAIL", "STRING", "BACKLOG", "RANDOM", "BYDURATION", "BYSIZE",
"BYCONNECTIONS", "CONNECTIONTIMEOUT", "MAXCONNECTIONS", "BYORDER",
"TRAFFICLOG", "OVER", "DECAY", "BINDTO", "THROUGHPUTLOG", "TYPE", "ANY",
"HTTP", "STICKYCOOKIE", "ADDCLIENTHEADER", "SETCLIENTHEADER",
"APPENDCLIENTHEADER", "ADDSERVERHEADER", "SETSERVERHEADER",
"APPENDSERVERHEADER", "ALLOWFROM", "DENYFROM", "ALLOWFILE", "DENYFILE",
- "'{'", "'}'", "';'", "input", "element", "service", "servicename",
- "servicestatements", "servicestatement", "servicebody", "portstatement",
- "bindstatement", "ipaddress", "number", "semicol", "verbositystatement",
- "onoff", "dispatchmodestatement", "opt_over", "overnumber",
- "dispatchmethod", "dispatchmethodspec", "revivingintervalstatement",
+ "EXTERNALHANDLER", "ONEND", "USERACCOUNT", "'{'", "'}'", "';'", "input",
+ "element", "service", "servicename", "servicestatements",
+ "servicestatement", "servicebody", "portstatement", "bindstatement",
+ "ipaddress", "number", "semicol", "verbositystatement", "onoff",
+ "dispatchmodestatement", "dispatchtail", "dispatchover", "overnumber",
+ "dispatchext", "dispatchmethod", "dispatchmethodspec",
+ "useraccountstatement", "useraccount", "revivingintervalstatement",
"backlogstatement", "shmkeystatement", "connectiontimeoutstatement",
"maxconnectionsstatement", "typestatement", "typespec", "typespecifier",
- "backendblock", "backendname", "backenddefinitions",
- "backenddefinition", "backendstatement", "serverstatement",
- "weightstatement", "decaystatement", "serveraddress",
- "onsuccessstatement", "onfailurestatement", "dumptrafficstatement",
- "throughputstatement", "commandline", "filename",
- "stickycookiestatement", "cookiespecifier", "addclientheaderstatement",
- "setclientheaderstatement", "appendclientheaderstatement",
- "addserverheaderstatement", "setserverheaderstatement",
- "appendserverheaderstatement", "headerstring", "headerstring_expected",
- "cookie_expected", "number_expected", "serveraddress_expected",
- "service_expected", "backendstatement_expected", "servicebody_expected",
- "semicol_expected", "onoff_expected", "dispatchmethod_expected",
- "commandline_expected", "filename_expected", "servicename_expected",
- "backendname_expected", "ipaddress_expected", "type_expected", 0
+ "allowfromstatement", "denyfromstatement", "allowfilestatement",
+ "denyfilestatement", "ipfilters", "backendblock", "backendname",
+ "backenddefinitions", "backenddefinition", "backendstatement",
+ "serverstatement", "weightstatement", "decaystatement", "serveraddress",
+ "onstartstatement", "onfailstatement", "onendstatement",
+ "dumptrafficstatement", "throughputstatement", "commandline",
+ "filename", "stickycookiestatement", "cookiespecifier",
+ "addclientheaderstatement", "setclientheaderstatement",
+ "appendclientheaderstatement", "addserverheaderstatement",
+ "setserverheaderstatement", "appendserverheaderstatement",
+ "headerstring", "headerstring_expected", "cookie_expected",
+ "number_expected", "serveraddress_expected", "service_expected",
+ "backendstatement_expected", "servicebody_expected", "semicol_expected",
+ "onoff_expected", "dispatchmethod_expected", "commandline_expected",
+ "filename_expected", "servicename_expected", "backendname_expected",
+ "ipaddress_expected", "type_expected", "ipfilters_expected",
+ "useraccount_expected", 0
};
#endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const short yyr1[] =
{
- 0, 50, 50, 51, 52, 53, 54, 54, 55, 56,
- 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
- 57, 58, 59, 60, 61, 62, 63, 63, 64, 65,
- 65, 66, 67, 68, 68, 68, 68, 68, 68, 69,
- 70, 71, 72, 73, 74, 75, 76, 76, 77, 78,
- 79, 79, 80, 81, 81, 81, 81, 81, 81, 81,
- 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
- 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
- 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
- 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116
+ 0, 53, 53, 54, 55, 56, 57, 57, 58, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 60, 61, 62, 63, 64,
+ 65, 66, 66, 67, 68, 68, 68, 69, 70, 71,
+ 72, 73, 73, 73, 73, 73, 73, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 91, 92, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -300,13 +364,15 @@ static const short yyr2[] =
{
0, 2, 1, 5, 2, 2, 2, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 3, 3, 2, 2, 2, 4, 1, 1, 4, 2,
- 0, 2, 2, 1, 1, 1, 1, 1, 1, 3,
- 3, 3, 3, 3, 3, 2, 1, 1, 5, 2,
- 2, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 3, 2, 2, 2,
+ 4, 1, 1, 4, 1, 1, 0, 2, 2, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 3, 2,
+ 3, 3, 3, 3, 3, 3, 2, 1, 1, 3,
+ 3, 3, 3, 2, 5, 2, 2, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 4, 3, 3, 1, 3, 3, 3, 3, 2, 2,
- 3, 2, 3, 3, 3, 3, 3, 3, 2, 0,
+ 1, 1, 1, 1, 1, 1, 1, 4, 3, 3,
+ 1, 3, 3, 3, 3, 3, 2, 2, 3, 2,
+ 3, 3, 3, 3, 3, 3, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
@@ -316,100 +382,120 @@ static const short yyr2[] =
error. */
static const short yydefact[] =
{
- 93, 2, 101, 0, 1, 0, 0, 4, 95, 5,
- 95, 7, 0, 3, 6, 91, 102, 97, 98, 91,
- 91, 91, 91, 91, 103, 104, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 96, 0,
- 0, 0, 0, 30, 0, 96, 96, 96, 96, 96,
- 96, 0, 96, 0, 20, 0, 23, 94, 49, 26,
- 27, 96, 91, 96, 33, 34, 35, 36, 38, 37,
- 32, 39, 41, 40, 42, 43, 21, 22, 44, 46,
- 47, 45, 24, 94, 51, 0, 25, 29, 0, 28,
- 48, 50, 92, 91, 99, 99, 100, 91, 100, 90,
- 89, 89, 89, 89, 89, 89, 54, 55, 62, 52,
- 53, 60, 61, 56, 57, 58, 59, 63, 64, 65,
- 66, 67, 68, 69, 31, 0, 96, 96, 0, 96,
- 96, 0, 96, 96, 96, 0, 96, 0, 96, 96,
- 96, 96, 96, 73, 96, 71, 74, 78, 75, 76,
- 79, 72, 77, 80, 81, 82, 88, 83, 84, 85,
- 86, 87, 70, 0, 0, 0
+ 111, 2, 119, 0, 1, 0, 0, 4, 113, 5,
+ 113, 7, 0, 3, 6, 109, 120, 115, 116, 109,
+ 109, 109, 109, 109, 121, 122, 123, 123, 118, 118,
+ 124, 8, 9, 10, 11, 12, 23, 13, 14, 15,
+ 16, 17, 18, 19, 21, 20, 22, 24, 114, 0,
+ 0, 0, 0, 36, 0, 114, 114, 114, 114, 114,
+ 114, 0, 114, 0, 114, 0, 114, 114, 0, 114,
+ 114, 0, 25, 0, 28, 112, 65, 31, 32, 114,
+ 109, 114, 34, 35, 39, 0, 41, 42, 43, 44,
+ 46, 45, 47, 40, 50, 52, 51, 53, 54, 26,
+ 27, 55, 57, 58, 56, 59, 63, 60, 61, 97,
+ 62, 48, 49, 29, 112, 67, 0, 30, 37, 0,
+ 33, 96, 64, 66, 110, 109, 117, 117, 118, 109,
+ 118, 108, 107, 107, 107, 107, 107, 107, 117, 70,
+ 71, 79, 68, 69, 77, 78, 72, 74, 73, 75,
+ 76, 80, 81, 82, 83, 84, 85, 86, 38, 0,
+ 114, 114, 114, 114, 114, 114, 114, 0, 114, 0,
+ 114, 114, 114, 114, 114, 114, 90, 114, 88, 91,
+ 92, 94, 89, 95, 98, 99, 100, 106, 101, 102,
+ 103, 104, 105, 93, 87, 0, 0, 0
};
static const short yydefgoto[] =
{
- 4, 1, 2, 5, 10, 11, 26, 27, 28, 50,
- 38, 54, 29, 61, 30, 63, 87, 43, 70, 31,
- 32, 33, 34, 35, 36, 52, 81, 37, 40, 83,
- 84, 109, 110, 111, 112, 144, 113, 114, 115, 116,
- 127, 130, 117, 134, 118, 119, 120, 121, 122, 123,
- 136, 137, 135, 39, 125, 3, 85, 12, 55, 42,
- 44, 128, 131, 6, 41, 51, 53
+ 4, 1, 2, 5, 10, 11, 31, 32, 33, 60,
+ 48, 72, 34, 79, 35, 81, 82, 118, 83, 53,
+ 93, 36, 70, 37, 38, 39, 40, 41, 42, 62,
+ 104, 43, 44, 45, 46, 64, 47, 50, 114, 115,
+ 142, 143, 144, 145, 177, 146, 147, 148, 149, 150,
+ 84, 67, 151, 166, 152, 153, 154, 155, 156, 157,
+ 168, 169, 167, 49, 159, 3, 116, 12, 73, 52,
+ 54, 85, 68, 6, 51, 61, 63, 65, 71
};
static const short yypact[] =
{
- -32768, 12,-32768, 14,-32768, -41, 18,-32768,-32768,-32768,
- -25,-32768, 41,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768, 5,-32768, 7,-32768, -19, 29,-32768,-32768,-32768,
+ -12,-32768, 20,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 24,
- -15, 30, -1, 7, 56,-32768,-32768,-32768,-32768,-32768,
- -32768, 19,-32768, -21,-32768, -12,-32768,-32768,-32768,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 32,
+ -8, 39, 12, -13, 34,-32768,-32768,-32768,-32768,-32768,
+ -32768, 25,-32768, 2,-32768, 30,-32768,-32768, 31,-32768,
+ -32768, 33,-32768, -4,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768, 35,-32768,-32768,-32768,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768, 4,-32768, 3,-32768,-32768, 44,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768, 8,-32768, 116,-32768,-32768, 54,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768,-32768,-32768, 28,-32768,-32768, 32,-32768,
- -32768, 35,-32768,-32768,-32768, 38,-32768, 39,-32768,-32768,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 43,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768, 48,-32768, 49,
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768, 59, 60,-32768
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768, 75, 76,-32768
};
static const short yypgoto[] =
{
- 62,-32768,-32768,-32768,-32768, 53,-32768, -20,-32768,-32768,
- 5, -45, -17,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768, -14,-32768,-32768,-32768,-32768,-32768,-32768,
- -19,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- -22, -28,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
- 2,-32768,-32768, 13,-32768,-32768,-32768,-32768,-32768,-32768,
- -32768,-32768,-32768,-32768,-32768,-32768,-32768
+ 77,-32768,-32768,-32768,-32768, 68,-32768, -37,-32768,-32768,
+ -2, -55, -36,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768, -34,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768, 56,-32768,-32768,-32768, -30,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -97, -16,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -63,-32768,-32768, 6,-32768,-32768,-32768,-32768,-32768,-32768,
+ -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768
};
-#define YYLAST 107
+#define YYLAST 164
static const short yytable[] =
{
- 71, 72, 73, 74, 75, 76, 8, 78, 15, 59,
- 60, 17, 92, 79, 80, -93, 86, 7, 89, 93,
- 94, 95, 9, 13, 45, 46, 47, 48, 49, 23,
- 56, 96, 57, 97, 58, 98, 62, 82, 77, 99,
- 100, 101, 102, 103, 104, 105, 15, 143, 16, 17,
- 124, 147, 90, 18, 150, 19, 20, 154, 156, 164,
- 165, 21, 163, 14, 91, 106, 22, 23, 107, 64,
- 133, 108, 24, 129, 25, 88, 0, 65, 66, 67,
- 68, 145, 146, 69, 148, 149, 0, 151, 152, 153,
- 0, 155, 0, 157, 158, 159, 160, 161, 126, 162,
- 0, 0, 132, 138, 139, 140, 141, 142
+ 94, 95, 96, 97, 98, 99, -117, 101, -111, 105,
+ 7, 107, 108, 69, 110, 111, 80, 55, 56, 57,
+ 58, 59, 77, 78, 117, 15, 120, 16, 17, 161,
+ 162, 8, 18, 9, 19, 20, 102, 103, 74, 13,
+ 21, 175, 75, 76, 100, 22, 23, 86, 113, 106,
+ 109, 24, 112, 25, 121, 87, 88, 89, 90, 122,
+ 158, 91, 176, 26, 27, 28, 29, 185, 187, 30,
+ 170, 171, 172, 173, 174, 196, 197, 195, 14, 139,
+ 140, 92, 141, 66, 123, 0, 119, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 178, 179, 180, 181, 182,
+ 183, 184, 163, 186, 165, 188, 189, 190, 191, 192,
+ 193, 15, 194, 160, 17, 124, 0, 164, 0, 0,
+ 0, 0, 125, 126, 127, 0, 0, 0, 0, 0,
+ 0, 0, 23, 0, 128, 0, 129, 0, 130, 0,
+ 0, 0, 131, 132, 133, 134, 135, 136, 137, 0,
+ 0, 0, 0, 0, 138
};
static const short yycheck[] =
{
- 45, 46, 47, 48, 49, 50, 47, 52, 5, 10,
- 11, 8, 9, 34, 35, 3, 61, 3, 63, 16,
- 17, 18, 4, 48, 19, 20, 21, 22, 23, 26,
- 6, 28, 47, 30, 4, 32, 29, 49, 19, 36,
- 37, 38, 39, 40, 41, 42, 5, 19, 7, 8,
- 6, 19, 48, 12, 19, 14, 15, 19, 19, 0,
- 0, 20, 0, 10, 83, 85, 25, 26, 85, 13,
- 98, 85, 31, 95, 33, 62, -1, 21, 22, 23,
- 24, 126, 127, 27, 129, 130, -1, 132, 133, 134,
- -1, 136, -1, 138, 139, 140, 141, 142, 93, 144,
- -1, -1, 97, 101, 102, 103, 104, 105
+ 55, 56, 57, 58, 59, 60, 19, 62, 3, 64,
+ 3, 66, 67, 29, 69, 70, 29, 19, 20, 21,
+ 22, 23, 10, 11, 79, 5, 81, 7, 8, 126,
+ 127, 50, 12, 4, 14, 15, 34, 35, 6, 51,
+ 20, 138, 50, 4, 19, 25, 26, 13, 52, 19,
+ 19, 31, 19, 33, 19, 21, 22, 23, 24, 51,
+ 6, 27, 19, 43, 44, 45, 46, 19, 19, 49,
+ 133, 134, 135, 136, 137, 0, 0, 0, 10, 116,
+ 116, 47, 116, 27, 114, -1, 80, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 160, 161, 162, 163, 164,
+ 165, 166, 128, 168, 130, 170, 171, 172, 173, 174,
+ 175, 5, 177, 125, 8, 9, -1, 129, -1, -1,
+ -1, -1, 16, 17, 18, -1, -1, -1, -1, -1,
+ -1, -1, 26, -1, 28, -1, 30, -1, 32, -1,
+ -1, -1, 36, 37, 38, 39, 40, 41, 42, -1,
+ -1, -1, -1, -1, 48
};
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
#line 3 "/sw/share/bison/bison.simple"
@@ -1119,7 +1205,7 @@ yyreduce:
switch (yyn) {
case 3:
-#line 102 "parser.y"
+#line 142 "parser.y"
{
/* Verify the service description, supply defaults
* and so on.
@@ -1144,14 +1230,14 @@ case 3:
;
break;}
case 5:
-#line 133 "parser.y"
+#line 173 "parser.y"
{
psmsg ("service:", yytext);
cur_service.name = xstrdup(yytext);
;
break;}
case 9:
-#line 152 "parser.y"
+#line 192 "parser.y"
{
pimsg ("sevice port:", yyvsp[0].set[0].v.ival);
cur_service.port = yyvsp[0].set[0].v.ival;
@@ -1159,7 +1245,7 @@ case 9:
;
break;}
case 10:
-#line 158 "parser.y"
+#line 198 "parser.y"
{
psmsg ("service binding:", yyvsp[0].set[0].v.sval);
cur_service.bind = yyvsp[0].set[0].v.sval;
@@ -1167,7 +1253,7 @@ case 10:
;
break;}
case 11:
-#line 164 "parser.y"
+#line 204 "parser.y"
{
pimsg ("service verbosity:", yyvsp[0].set[0].v.ival);
cur_service.verbosity = yyvsp[0].set[0].v.ival;
@@ -1175,17 +1261,19 @@ case 11:
;
break;}
case 12:
-#line 170 "parser.y"
+#line 210 "parser.y"
{
pimsg ("service dispatch mode:", yyvsp[0].set[0].v.ival);
pimsg ("service dispatch over:", lastovernr);
+ psmsg ("service dispatch exth:", lastext);
cur_service.dispatchtype = yyvsp[0].set[0].v.ival;
cur_service.dispatchover = lastovernr;
+ cur_service.dispatchext = lastext;
free (yyvsp[0].set);
;
break;}
case 13:
-#line 178 "parser.y"
+#line 220 "parser.y"
{
pimsg ("service revival interval:", yyvsp[0].set[0].v.ival);
cur_service.rev_interval = yyvsp[0].set[0].v.ival;
@@ -1193,7 +1281,7 @@ case 13:
;
break;}
case 14:
-#line 184 "parser.y"
+#line 226 "parser.y"
{
pimsg ("service backlog:", yyvsp[0].set[0].v.ival);
cur_service.backlog = yyvsp[0].set[0].v.ival;
@@ -1201,7 +1289,7 @@ case 14:
;
break;}
case 15:
-#line 190 "parser.y"
+#line 232 "parser.y"
{
pimsg ("service shmkey:", yyvsp[0].set[0].v.ival);
cur_service.shmkey = yyvsp[0].set[0].v.ival;
@@ -1209,7 +1297,7 @@ case 15:
;
break;}
case 16:
-#line 196 "parser.y"
+#line 238 "parser.y"
{
pimsg ("connection timout:", yyvsp[0].set[0].v.ival);
cur_service.connectiontimeout = yyvsp[0].set[0].v.ival;
@@ -1217,7 +1305,7 @@ case 16:
;
break;}
case 17:
-#line 202 "parser.y"
+#line 244 "parser.y"
{
pimsg ("max clients in service:", yyvsp[0].set[0].v.ival);
cur_service.maxconnections = yyvsp[0].set[0].v.ival;
@@ -1225,7 +1313,7 @@ case 17:
;
break;}
case 18:
-#line 208 "parser.y"
+#line 250 "parser.y"
{
pimsg ("service type: ", yyvsp[0].set[0].v.ival);
cur_service.type = yyvsp[0].set[0].v.ival;
@@ -1233,7 +1321,48 @@ case 18:
;
break;}
case 19:
-#line 229 "parser.y"
+#line 256 "parser.y"
+{
+ psmsg ("allow from: ", yyvsp[0].set[0].v.sval);
+ add_allowfrom (yyvsp[0].set[0].v.sval);
+ free (yyvsp[0].set);
+ ;
+ break;}
+case 20:
+#line 262 "parser.y"
+{
+ psmsg ("allow file: ", yyvsp[0].set[0].v.sval);
+ cur_service.allowfile = yyvsp[0].set[0].v.sval;
+ free (yyvsp[0].set);
+ ;
+ break;}
+case 21:
+#line 268 "parser.y"
+{
+ psmsg ("deny from: ", yyvsp[0].set[0].v.sval);
+ add_denyfrom (yyvsp[0].set[0].v.sval);
+ free (yyvsp[0].set);
+ ;
+ break;}
+case 22:
+#line 274 "parser.y"
+{
+ psmsg ("deny file: ", yyvsp[0].set[0].v.sval);
+ cur_service.denyfile = yyvsp[0].set[0].v.sval;
+ free (yyvsp[0].set);
+ ;
+ break;}
+case 23:
+#line 280 "parser.y"
+{
+ psmsg ("user account: ", yyvsp[0].set[0].v.sval);
+ setuseraccount (yyvsp[0].set[0].v.sval);
+ free (yyvsp[0].set[0].v.sval);
+ free (yyvsp[0].set);
+ ;
+ break;}
+case 24:
+#line 287 "parser.y"
{
pimsg ("converting backend statements, count is", yyvsp[0].n);
for (i = 0; i < yyvsp[0].n; i++)
@@ -1252,13 +1381,17 @@ case 19:
pimsg ("backend block verbosity:", yyvsp[0].set[i].v.ival);
cur_backend.verbosity = yyvsp[0].set[i].v.ival;
break;
- case cf_onsuccspec:
- psmsg ("backend block onsuccess:", yyvsp[0].set[i].v.sval);
- cur_backend.onsuccess = yyvsp[0].set[i].v.sval;
+ case cf_onstartspec:
+ psmsg ("backend block onstart:", yyvsp[0].set[i].v.sval);
+ cur_backend.onstart = yyvsp[0].set[i].v.sval;
break;
case cf_onfailspec:
- psmsg ("backend block onfailure:", yyvsp[0].set[i].v.sval);
- cur_backend.onfailure = yyvsp[0].set[i].v.sval;
+ psmsg ("backend block onfail:", yyvsp[0].set[i].v.sval);
+ cur_backend.onfail = yyvsp[0].set[i].v.sval;
+ break;
+ case cf_onendspec:
+ psmsg ("backend block onend:", yyvsp[0].set[i].v.sval);
+ cur_backend.onend = yyvsp[0].set[i].v.sval;
break;
case cf_dumpspec:
psmsg ("backend trafficlog:", yyvsp[0].set[i].v.sval);
@@ -1361,7 +1494,7 @@ case 19:
yyvsp[0].set[i].cf);
}
free (yyvsp[0].set);
-
+
/* Verify the backend block, supply defaults,
* And so on.
*/
@@ -1381,8 +1514,8 @@ case 19:
memset (&cur_backend, 0, sizeof(cur_backend));
;
break;}
-case 20:
-#line 380 "parser.y"
+case 25:
+#line 442 "parser.y"
{
pimsg ("port statement:", lastnr);
yyval.n = 1;
@@ -1391,8 +1524,8 @@ case 20:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 21:
-#line 392 "parser.y"
+case 26:
+#line 454 "parser.y"
{
psmsg ("bindto statement:", laststr);
yyval.n = 1;
@@ -1401,22 +1534,22 @@ case 21:
yyval.set[0].v.sval = xstrdup(laststr);
;
break;}
-case 22:
-#line 403 "parser.y"
+case 27:
+#line 465 "parser.y"
{
setlaststr (laststring);
free (laststring);
laststring = 0;
;
break;}
-case 23:
-#line 412 "parser.y"
+case 28:
+#line 474 "parser.y"
{
setlastnr (yytext);
;
break;}
-case 25:
-#line 426 "parser.y"
+case 30:
+#line 488 "parser.y"
{
pimsg ("verbosity statement:", lastnr);
yyval.n = 1;
@@ -1425,20 +1558,26 @@ case 25:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 26:
-#line 436 "parser.y"
+case 31:
+#line 498 "parser.y"
{
lastnr = 1;
;
break;}
-case 27:
-#line 440 "parser.y"
+case 32:
+#line 502 "parser.y"
{
lastnr = 0;
;
break;}
-case 28:
-#line 449 "parser.y"
+case 33:
+#line 511 "parser.y"
+{
+ yyval = yyvsp[-2];
+ ;
+ break;}
+case 37:
+#line 526 "parser.y"
{
pimsg ("dispatch mode statement:", lastnr);
yyval.n = 1;
@@ -1453,56 +1592,92 @@ case 28:
cur_service.name);
;
break;}
-case 30:
-#line 468 "parser.y"
+case 38:
+#line 543 "parser.y"
+{
+ setlastovernr (yytext);
+ ;
+ break;}
+case 39:
+#line 549 "parser.y"
{
- lastovernr = 0;
+ psmsg ("external handler:", laststr);
+ if (lastnr != ds_externalhandler)
+ error ("Service %s: this dispatch mode doesn't support "
+ "an external handler", cur_service.name);
+ setlastext (laststr);
;
break;}
-case 31:
-#line 475 "parser.y"
+case 40:
+#line 560 "parser.y"
{
- setlastovernr (yytext);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].v.ival = lastnr;
;
break;}
-case 33:
-#line 486 "parser.y"
+case 41:
+#line 568 "parser.y"
{
lastnr = ds_roundrobin;
;
break;}
-case 34:
-#line 490 "parser.y"
+case 42:
+#line 572 "parser.y"
{
lastnr = ds_random;
;
break;}
-case 35:
-#line 494 "parser.y"
+case 43:
+#line 576 "parser.y"
{
lastnr = ds_byduration;
;
break;}
-case 36:
-#line 498 "parser.y"
+case 44:
+#line 580 "parser.y"
{
lastnr = ds_bysize;
;
break;}
-case 37:
-#line 502 "parser.y"
+case 45:
+#line 584 "parser.y"
{
lastnr = ds_byorder;
;
break;}
-case 38:
-#line 506 "parser.y"
+case 46:
+#line 588 "parser.y"
{
lastnr = ds_byconnections;
;
break;}
-case 39:
-#line 514 "parser.y"
+case 47:
+#line 592 "parser.y"
+{
+ lastnr = ds_externalhandler;
+ ;
+ break;}
+case 48:
+#line 600 "parser.y"
+{
+ pimsg ("user account statement:", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].cf = cf_useraccountspec;
+ yyval.set[0].v.sval = xstrdup (laststr);
+ ;
+ break;}
+case 49:
+#line 611 "parser.y"
+{
+ setlaststr (laststring);
+ free (laststring);
+ laststring = 0;
+ ;
+ break;}
+case 50:
+#line 621 "parser.y"
{
pimsg ("reviving interval statement:", lastnr);
yyval.n = 1;
@@ -1511,8 +1686,8 @@ case 39:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 40:
-#line 526 "parser.y"
+case 51:
+#line 633 "parser.y"
{
pimsg ("backlog statement:", lastnr);
yyval.n = 1;
@@ -1521,8 +1696,8 @@ case 40:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 41:
-#line 538 "parser.y"
+case 52:
+#line 645 "parser.y"
{
pimsg ("shmkey statement:", lastnr);
yyval.n = 1;
@@ -1531,8 +1706,8 @@ case 41:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 42:
-#line 550 "parser.y"
+case 53:
+#line 657 "parser.y"
{
pimsg ("connection timeout statement:", lastnr);
yyval.n = 1;
@@ -1541,8 +1716,8 @@ case 42:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 43:
-#line 562 "parser.y"
+case 54:
+#line 669 "parser.y"
{
pimsg ("max clients statement (service):", lastnr);
yyval.n = 1;
@@ -1551,8 +1726,8 @@ case 43:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 44:
-#line 574 "parser.y"
+case 55:
+#line 681 "parser.y"
{
pimsg ("service type:", lastnr);
yyval.n = 1;
@@ -1561,33 +1736,81 @@ case 44:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 46:
-#line 589 "parser.y"
+case 57:
+#line 696 "parser.y"
{
lastnr = type_any;
;
break;}
-case 47:
-#line 593 "parser.y"
+case 58:
+#line 700 "parser.y"
{
lastnr = type_http;
;
break;}
-case 48:
-#line 603 "parser.y"
+case 59:
+#line 708 "parser.y"
+{
+ psmsg ("allow from: ", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].cf = cf_allowfromspec;
+ yyval.set[0].v.sval = xstrdup(laststr);
+ ;
+ break;}
+case 60:
+#line 720 "parser.y"
+{
+ psmsg ("allow from: ", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].cf = cf_denyfromspec;
+ yyval.set[0].v.sval = xstrdup(laststr);
+ ;
+ break;}
+case 61:
+#line 732 "parser.y"
+{
+ psmsg ("allow file: ", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].cf = cf_allowfilespec;
+ yyval.set[0].v.sval = xstrdup(laststr);
+ ;
+ break;}
+case 62:
+#line 744 "parser.y"
+{
+ psmsg ("allow file: ", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof(Confset));
+ yyval.set[0].cf = cf_allowfilespec;
+ yyval.set[0].v.sval = xstrdup(laststr);
+ ;
+ break;}
+case 63:
+#line 755 "parser.y"
+{
+ setlaststr (laststring);
+ free (laststring);
+ laststring = 0;
+ ;
+ break;}
+case 64:
+#line 767 "parser.y"
{
yyval = yyvsp[-1];
;
break;}
-case 49:
-#line 610 "parser.y"
+case 65:
+#line 774 "parser.y"
{
psmsg ("backend name:", yytext);
cur_backend.name = xstrdup (yytext);
;
break;}
-case 50:
-#line 618 "parser.y"
+case 66:
+#line 782 "parser.y"
{
yyvsp[-1].n++;
yyvsp[-1].set = xrealloc (yyvsp[-1].set, yyvsp[-1].n * sizeof(Confset));
@@ -1595,139 +1818,146 @@ case 50:
yyval = yyvsp[-1];
;
break;}
-case 51:
-#line 625 "parser.y"
+case 67:
+#line 789 "parser.y"
{
yyval = yyvsp[0];
;
break;}
-case 52:
-#line 632 "parser.y"
+case 68:
+#line 796 "parser.y"
{
yyval = yyvsp[0];
;
break;}
-case 53:
-#line 638 "parser.y"
+case 69:
+#line 802 "parser.y"
{
psmsg ("backend server:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 54:
-#line 643 "parser.y"
+case 70:
+#line 807 "parser.y"
{
pimsg ("backend port:", yyvsp[0].set[0].v.ival);
yyval = yyvsp[0];
;
break;}
-case 55:
-#line 648 "parser.y"
+case 71:
+#line 812 "parser.y"
{
pimsg ("backend verbosity:", yyvsp[0].set[0].v.ival);
yyval = yyvsp[0];
;
break;}
-case 56:
-#line 653 "parser.y"
+case 72:
+#line 817 "parser.y"
{
- psmsg ("backend onsuccess:", yyvsp[0].set[0].v.sval);
+ psmsg ("backend onstart:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 57:
-#line 658 "parser.y"
+case 73:
+#line 822 "parser.y"
{
- psmsg ("backend onfailure:", yyvsp[0].set[0].v.sval);
+ psmsg ("backend onend:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 58:
-#line 663 "parser.y"
+case 74:
+#line 827 "parser.y"
+{
+ psmsg ("backend onfail:", yyvsp[0].set[0].v.sval);
+ yyval = yyvsp[0];
+ ;
+ break;}
+case 75:
+#line 832 "parser.y"
{
psmsg ("backend trafficlog:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 59:
-#line 668 "parser.y"
+case 76:
+#line 837 "parser.y"
{
psmsg ("backend trafficlog:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 60:
-#line 673 "parser.y"
+case 77:
+#line 842 "parser.y"
{
pimsg ("backend weight:", yyvsp[0].set[0].v.ival);
yyval = yyvsp[0];
;
break;}
-case 61:
-#line 678 "parser.y"
+case 78:
+#line 847 "parser.y"
{
pimsg ("backend decay:", yyvsp[0].set[0].v.ival);
yyval = yyvsp[0];
;
break;}
-case 62:
-#line 683 "parser.y"
+case 79:
+#line 852 "parser.y"
{
pimsg ("backend maxconnections:", yyvsp[0].set[0].v.ival);
yyval = yyvsp[0];
;
break;}
-case 63:
-#line 688 "parser.y"
+case 80:
+#line 857 "parser.y"
{
psmsg ("backend sticky cookie:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 64:
-#line 693 "parser.y"
+case 81:
+#line 862 "parser.y"
{
psmsg ("addclientheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 65:
-#line 698 "parser.y"
+case 82:
+#line 867 "parser.y"
{
psmsg ("setclientheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 66:
-#line 703 "parser.y"
+case 83:
+#line 872 "parser.y"
{
psmsg ("appendclientheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 67:
-#line 708 "parser.y"
+case 84:
+#line 877 "parser.y"
{
psmsg ("addserverheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 68:
-#line 713 "parser.y"
+case 85:
+#line 882 "parser.y"
{
psmsg ("setserverheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 69:
-#line 718 "parser.y"
+case 86:
+#line 887 "parser.y"
{
psmsg ("appendserverheader:", yyvsp[0].set[0].v.sval);
yyval = yyvsp[0];
;
break;}
-case 70:
-#line 728 "parser.y"
+case 87:
+#line 897 "parser.y"
{
psmsg ("server statement:", laststr);
yyval.n = 1;
@@ -1736,8 +1966,8 @@ case 70:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 71:
-#line 740 "parser.y"
+case 88:
+#line 909 "parser.y"
{
pimsg ("weight statement", lastnr);
yyval.n = 1;
@@ -1746,8 +1976,8 @@ case 71:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 72:
-#line 752 "parser.y"
+case 89:
+#line 921 "parser.y"
{
pimsg ("decay statement", lastnr);
yyval.n = 1;
@@ -1756,34 +1986,44 @@ case 72:
yyval.set[0].v.ival = lastnr;
;
break;}
-case 73:
-#line 762 "parser.y"
+case 90:
+#line 931 "parser.y"
{
setlaststr (laststring);
;
break;}
-case 74:
-#line 770 "parser.y"
+case 91:
+#line 939 "parser.y"
{
- psmsg ("onsuccess statement:", laststr);
+ psmsg ("onstart statement:", laststr);
yyval.n = 1;
yyval.set = xmalloc (sizeof (Confset));
- yyval.set[0].cf = cf_onsuccspec;
+ yyval.set[0].cf = cf_onstartspec;
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 75:
-#line 782 "parser.y"
+case 92:
+#line 951 "parser.y"
{
- psmsg ("onfailure statement:", laststr);
+ psmsg ("onfail statement:", laststr);
yyval.n = 1;
yyval.set = xmalloc (sizeof (Confset));
yyval.set[0].cf = cf_onfailspec;
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 76:
-#line 794 "parser.y"
+case 93:
+#line 963 "parser.y"
+{
+ psmsg ("onend statement:", laststr);
+ yyval.n = 1;
+ yyval.set = xmalloc (sizeof (Confset));
+ yyval.set[0].cf = cf_onendspec;
+ yyval.set[0].v.sval = xstrdup (laststr);
+ ;
+ break;}
+case 94:
+#line 975 "parser.y"
{
psmsg ("trafficlog statement:", laststr);
yyval.n = 1;
@@ -1792,8 +2032,8 @@ case 76:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 77:
-#line 806 "parser.y"
+case 95:
+#line 987 "parser.y"
{
psmsg ("throughputlog statement:", laststr);
yyval.n = 1;
@@ -1802,24 +2042,24 @@ case 77:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 78:
-#line 817 "parser.y"
+case 96:
+#line 998 "parser.y"
{
setlaststr (laststring);
free (laststring);
laststring = 0;
;
break;}
-case 79:
-#line 826 "parser.y"
+case 97:
+#line 1007 "parser.y"
{
setlaststr (laststring);
free (laststring);
laststring = 0;
;
break;}
-case 80:
-#line 836 "parser.y"
+case 98:
+#line 1017 "parser.y"
{
psmsg ("insertcookie statement:", laststr);
yyval.n = 1;
@@ -1828,16 +2068,16 @@ case 80:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 81:
-#line 847 "parser.y"
+case 99:
+#line 1028 "parser.y"
{
setlaststr (laststring);
free (laststring);
laststring = 0;
;
break;}
-case 82:
-#line 857 "parser.y"
+case 100:
+#line 1038 "parser.y"
{
psmsg ("addclientheader statement:", laststr);
yyval.n = 1;
@@ -1846,8 +2086,8 @@ case 82:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 83:
-#line 869 "parser.y"
+case 101:
+#line 1050 "parser.y"
{
psmsg ("setclientheader statement:", laststr);
yyval.n = 1;
@@ -1856,8 +2096,8 @@ case 83:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 84:
-#line 881 "parser.y"
+case 102:
+#line 1062 "parser.y"
{
psmsg ("appendclientheader statement:", laststr);
yyval.n = 1;
@@ -1866,8 +2106,8 @@ case 84:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 85:
-#line 893 "parser.y"
+case 103:
+#line 1074 "parser.y"
{
psmsg ("addserverheader statement:", laststr);
yyval.n = 1;
@@ -1876,8 +2116,8 @@ case 85:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 86:
-#line 905 "parser.y"
+case 104:
+#line 1086 "parser.y"
{
psmsg ("setserverheader statement:", laststr);
yyval.n = 1;
@@ -1886,8 +2126,8 @@ case 86:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 87:
-#line 917 "parser.y"
+case 105:
+#line 1098 "parser.y"
{
psmsg ("appendserverheader statement:", laststr);
yyval.n = 1;
@@ -1896,110 +2136,122 @@ case 87:
yyval.set[0].v.sval = xstrdup (laststr);
;
break;}
-case 88:
-#line 928 "parser.y"
+case 106:
+#line 1109 "parser.y"
{
setlaststr (laststring);
free (laststring);
laststring = 0;
;
break;}
-case 89:
-#line 935 "parser.y"
+case 107:
+#line 1116 "parser.y"
{
yyerrmsg = "HTTP header specifier expected";
;
break;}
-case 90:
-#line 940 "parser.y"
+case 108:
+#line 1121 "parser.y"
{
yyerrmsg = "cookie specifier expected";
;
break;}
-case 91:
-#line 945 "parser.y"
+case 109:
+#line 1126 "parser.y"
{
yyerrmsg = "number expected";
;
break;}
-case 92:
-#line 950 "parser.y"
+case 110:
+#line 1131 "parser.y"
{
yyerrmsg = "hostname or IP address expected";
;
break;}
-case 93:
-#line 955 "parser.y"
+case 111:
+#line 1136 "parser.y"
{
yyerrmsg = "'service' expected";
;
break;}
-case 94:
-#line 960 "parser.y"
+case 112:
+#line 1141 "parser.y"
{
yyerrmsg = "backend definition statement expected";
;
break;}
-case 95:
-#line 965 "parser.y"
+case 113:
+#line 1146 "parser.y"
{
yyerrmsg = "service body statement expected";
;
break;}
-case 96:
-#line 970 "parser.y"
+case 114:
+#line 1151 "parser.y"
{
yyerrmsg = "semicolon (;) expected";
;
break;}
-case 97:
-#line 975 "parser.y"
+case 115:
+#line 1156 "parser.y"
{
yyerrmsg = "'on' or 'off' expetcted";
;
break;}
-case 98:
-#line 980 "parser.y"
+case 116:
+#line 1161 "parser.y"
{
yyerrmsg = "dispatch method expected";
;
break;}
-case 99:
-#line 985 "parser.y"
+case 117:
+#line 1166 "parser.y"
{
yyerrmsg = "command line expected";
;
break;}
-case 100:
-#line 990 "parser.y"
+case 118:
+#line 1171 "parser.y"
{
yyerrmsg = "file name expected";
;
break;}
-case 101:
-#line 995 "parser.y"
+case 119:
+#line 1176 "parser.y"
{
yyerrmsg = "service name (identifier) expected";
;
break;}
-case 102:
-#line 1000 "parser.y"
+case 120:
+#line 1181 "parser.y"
{
yyerrmsg = "backend name (identifier) expected";
;
break;}
-case 103:
-#line 1005 "parser.y"
+case 121:
+#line 1186 "parser.y"
{
yyerrmsg = "IP address or 'any' expected";
;
break;}
-case 104:
-#line 1010 "parser.y"
+case 122:
+#line 1191 "parser.y"
{
yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)";
;
break;}
+case 123:
+#line 1196 "parser.y"
+{
+ yyerrmsg = "IP filter(s) expected";
+;
+ break;}
+case 124:
+#line 1201 "parser.y"
+{
+ yyerrmsg = "username expected";
+;
+ break;}
}
#line 705 "/sw/share/bison/bison.simple"
@@ -2233,4 +2485,4 @@ yyreturn:
#endif
return yyresult;
}
-#line 1014 "parser.y"
+#line 1205 "parser.y"
diff --git a/src/parser.h b/src/parser.h
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#ifndef BISON_PARSER_H
# define BISON_PARSER_H
@@ -19,8 +24,8 @@
# define REVIVINGINTERVAL 268
# define SHMKEY 269
# define WEIGHT 270
-# define ONSUCCESS 271
-# define ONFAILURE 272
+# define ONSTART 271
+# define ONFAIL 272
# define STRING 273
# define BACKLOG 274
# define RANDOM 275
@@ -49,6 +54,9 @@
# define DENYFROM 298
# define ALLOWFILE 299
# define DENYFILE 300
+# define EXTERNALHANDLER 301
+# define ONEND 302
+# define USERACCOUNT 303
extern YYSTYPE yylval;
diff --git a/src/parser.y b/src/parser.y
@@ -6,8 +6,13 @@
#define YYSTYPE Confsets
+/* Static parser vars */
+static int i; /* Loop counter */
+static Backend cur_backend; /* Storage for a handled backend */
+static Service cur_service; /* Storage for a handled service */
+
/* Parser debugging related */
-// #define PARSER_DEBUG
+// #define PARSER_DEBUG
#ifdef PARSER_DEBUG
static void pmsg (char const *x) {
printf ("P: %s\n", x);
@@ -24,13 +29,13 @@
# define pimsg(x,y)
#endif
-/* Error handler for yyparse() */
+/* Error handler for yyparse() */
static int yyerror (char *msg) {
error ("Parse error at line %d, '%s': %s",
yylineno + 1, yytext, yyerrmsg);
}
-/* Store an encountered string */
+/* Store an encountered string */
static char *laststr;
static void setlaststr (char const *what) {
free (laststr);
@@ -49,6 +54,13 @@ static void setlastovernr (char const *what) {
lastovernr = atoi(what);
}
+/* Store an encountered 'externalhandler' */
+static char *lastext;
+static void setlastext (char const *what) {
+ free (lastext);
+ lastext = xstrdup (what);
+}
+
/* Get the server part from HOSTNAME:PORT in allocated memory */
static char *serverpart (char const *what) {
char *ret = xstrdup (what);
@@ -68,21 +80,49 @@ static int portpart (char const *what) {
return (0);
}
-/* Temp vars */
-static int i; /* Loop counter */
-static Backend cur_backend; /* Storage for a handled backend */
-static Service cur_service; /* Storage for a handled service */
+/* Add a list of IP filters to the allowlist or the denylist */
+static void add_any (char *what, int chain) {
+ char *item;
+ int result;
+ for (item = strtok (what, " "); item; item = strtok (0, " ")) {
+ if (chain)
+ result = ipf_add_allow (&cur_service, item);
+ else
+ result = ipf_add_deny (&cur_service, item);
+ if (result)
+ error ("Bad IP filter specifier '%s' at line %d",
+ item, yylineno + 1);
+ }
+}
+static void add_allowfrom (char *what) {
+ add_any (what, 1);
+}
+static void add_denyfrom (char *what) {
+ add_any (what, 0);
+}
+
+/* Set uid/gid for external commands. */
+static void setuseraccount (char *username) {
+ struct passwd *pw;
+
+ if (! (pw = getpwnam (username)) )
+ error ("Invalid username '%s' at line %d", username, yylineno + 1);
+ cur_service.uid = pw->pw_uid;
+ cur_service.gid = pw->pw_gid;
+}
+
%}
/* Declarations */
%token SERVICE IDENTIFIER PORT NUMBER BACKEND VERBOSITY SERVER
ON OFF DISPATCHMODE ROUNDROBIN REVIVINGINTERVAL SHMKEY WEIGHT
- ONSUCCESS ONFAILURE STRING BACKLOG RANDOM BYDURATION BYSIZE
+ ONSTART ONFAIL STRING BACKLOG RANDOM BYDURATION BYSIZE
BYCONNECTIONS CONNECTIONTIMEOUT MAXCONNECTIONS BYORDER TRAFFICLOG
OVER DECAY BINDTO THROUGHPUTLOG TYPE ANY HTTP
STICKYCOOKIE ADDCLIENTHEADER SETCLIENTHEADER APPENDCLIENTHEADER
ADDSERVERHEADER SETSERVERHEADER APPENDSERVERHEADER
- ALLOWFROM DENYFROM ALLOWFILE DENYFILE
+ ALLOWFROM DENYFROM ALLOWFILE DENYFILE EXTERNALHANDLER ONEND
+ USERACCOUNT
%%
/* Config file grammar rules */
@@ -154,7 +194,7 @@ servicebody:
cur_service.port = $1.set[0].v.ival;
free ($1.set);
}
-|
+|
bindstatement {
psmsg ("service binding:", $1.set[0].v.sval);
cur_service.bind = $1.set[0].v.sval;
@@ -170,8 +210,10 @@ servicebody:
dispatchmodestatement {
pimsg ("service dispatch mode:", $1.set[0].v.ival);
pimsg ("service dispatch over:", lastovernr);
+ psmsg ("service dispatch exth:", lastext);
cur_service.dispatchtype = $1.set[0].v.ival;
cur_service.dispatchover = lastovernr;
+ cur_service.dispatchext = lastext;
free ($1.set);
}
|
@@ -210,22 +252,38 @@ servicebody:
cur_service.type = $1.set[0].v.ival;
free ($1.set);
}
-/* |
+|
allowfromstatement {
psmsg ("allow from: ", $1.set[0].v.sval);
- if (ipf_add_allow (&cur_service, $1.set[0].v.sval))
- error ("Bad IP filter specifier '%s' at line %d",
- $1.set[0].v.sval, yylineno + 1);
+ add_allowfrom ($1.set[0].v.sval);
+ free ($1.set);
+ }
+|
+ allowfilestatement {
+ psmsg ("allow file: ", $1.set[0].v.sval);
+ cur_service.allowfile = $1.set[0].v.sval;
+ free ($1.set);
}
|
denyfromstatement {
psmsg ("deny from: ", $1.set[0].v.sval);
- if (ipf_add_deny (&cur_service, $1.set[0].v.sval))
- error ("Bad IP filter specifier '%s' at line %d",
- $1.set[0].v.sval, yylineno + 1);
+ add_denyfrom ($1.set[0].v.sval);
+ free ($1.set);
}
-*/
-|
+|
+ denyfilestatement {
+ psmsg ("deny file: ", $1.set[0].v.sval);
+ cur_service.denyfile = $1.set[0].v.sval;
+ free ($1.set);
+ }
+|
+ useraccountstatement {
+ psmsg ("user account: ", $1.set[0].v.sval);
+ setuseraccount ($1.set[0].v.sval);
+ free ($1.set[0].v.sval);
+ free ($1.set);
+ }
+|
backendblock {
pimsg ("converting backend statements, count is", $1.n);
for (i = 0; i < $1.n; i++)
@@ -244,13 +302,17 @@ servicebody:
pimsg ("backend block verbosity:", $1.set[i].v.ival);
cur_backend.verbosity = $1.set[i].v.ival;
break;
- case cf_onsuccspec:
- psmsg ("backend block onsuccess:", $1.set[i].v.sval);
- cur_backend.onsuccess = $1.set[i].v.sval;
+ case cf_onstartspec:
+ psmsg ("backend block onstart:", $1.set[i].v.sval);
+ cur_backend.onstart = $1.set[i].v.sval;
break;
case cf_onfailspec:
- psmsg ("backend block onfailure:", $1.set[i].v.sval);
- cur_backend.onfailure = $1.set[i].v.sval;
+ psmsg ("backend block onfail:", $1.set[i].v.sval);
+ cur_backend.onfail = $1.set[i].v.sval;
+ break;
+ case cf_onendspec:
+ psmsg ("backend block onend:", $1.set[i].v.sval);
+ cur_backend.onend = $1.set[i].v.sval;
break;
case cf_dumpspec:
psmsg ("backend trafficlog:", $1.set[i].v.sval);
@@ -353,7 +415,7 @@ servicebody:
$1.set[i].cf);
}
free ($1.set);
-
+
/* Verify the backend block, supply defaults,
* And so on.
*/
@@ -445,8 +507,23 @@ onoff:
dispatchmodestatement:
DISPATCHMODE
dispatchmethod
- opt_over
+ dispatchtail
semicol {
+ $$ = $2;
+ }
+;
+
+dispatchtail:
+ dispatchover
+ |
+ dispatchext
+ |
+ /* empty */
+;
+
+dispatchover:
+ OVER
+ overnumber {
pimsg ("dispatch mode statement:", lastnr);
$$.n = 1;
$$.set = xmalloc (sizeof(Confset));
@@ -461,15 +538,6 @@ dispatchmodestatement:
}
;
-opt_over:
- OVER
- overnumber
-|
- /* empty */ {
- lastovernr = 0;
- }
-;
-
overnumber:
number_expected
NUMBER {
@@ -477,9 +545,23 @@ overnumber:
}
;
+dispatchext:
+ commandline {
+ psmsg ("external handler:", laststr);
+ if (lastnr != ds_externalhandler)
+ error ("Service %s: this dispatch mode doesn't support "
+ "an external handler", cur_service.name);
+ setlastext (laststr);
+ }
+;
+
dispatchmethod:
dispatchmethod_expected
- dispatchmethodspec
+ dispatchmethodspec {
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].v.ival = lastnr;
+ }
;
dispatchmethodspec:
@@ -506,6 +588,31 @@ dispatchmethodspec:
BYCONNECTIONS {
lastnr = ds_byconnections;
}
+|
+ EXTERNALHANDLER {
+ lastnr = ds_externalhandler;
+ }
+;
+
+useraccountstatement:
+ USERACCOUNT
+ useraccount
+ semicol {
+ pimsg ("user account statement:", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].cf = cf_useraccountspec;
+ $$.set[0].v.sval = xstrdup (laststr);
+ }
+;
+
+useraccount:
+ useraccount_expected
+ STRING {
+ setlaststr (laststring);
+ free (laststring);
+ laststring = 0;
+ }
;
revivingintervalstatement:
@@ -578,7 +685,7 @@ typestatement:
$$.set[0].cf = cf_typespec;
$$.set[0].v.ival = lastnr;
}
- ;
+;
typespec:
type_expected
@@ -595,6 +702,63 @@ typespecifier:
}
;
+allowfromstatement:
+ ALLOWFROM
+ ipfilters
+ semicol {
+ psmsg ("allow from: ", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].cf = cf_allowfromspec;
+ $$.set[0].v.sval = xstrdup(laststr);
+ }
+;
+
+denyfromstatement:
+ DENYFROM
+ ipfilters
+ semicol {
+ psmsg ("allow from: ", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].cf = cf_denyfromspec;
+ $$.set[0].v.sval = xstrdup(laststr);
+ }
+;
+
+allowfilestatement:
+ ALLOWFILE
+ filename
+ semicol {
+ psmsg ("allow file: ", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].cf = cf_allowfilespec;
+ $$.set[0].v.sval = xstrdup(laststr);
+ }
+;
+
+denyfilestatement:
+ DENYFILE
+ filename
+ semicol {
+ psmsg ("allow file: ", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof(Confset));
+ $$.set[0].cf = cf_allowfilespec;
+ $$.set[0].v.sval = xstrdup(laststr);
+ }
+;
+
+ipfilters:
+ ipfilters_expected
+ STRING {
+ setlaststr (laststring);
+ free (laststring);
+ laststring = 0;
+ }
+;
+
backendblock:
BACKEND
backendname
@@ -638,7 +802,7 @@ backendstatement:
serverstatement {
psmsg ("backend server:", $1.set[0].v.sval);
$$ = $1;
- }
+ }
|
portstatement {
pimsg ("backend port:", $1.set[0].v.ival);
@@ -650,13 +814,18 @@ backendstatement:
$$ = $1;
}
|
- onsuccessstatement {
- psmsg ("backend onsuccess:", $1.set[0].v.sval);
+ onstartstatement {
+ psmsg ("backend onstart:", $1.set[0].v.sval);
+ $$ = $1;
+ }
+|
+ onendstatement {
+ psmsg ("backend onend:", $1.set[0].v.sval);
$$ = $1;
}
|
- onfailurestatement {
- psmsg ("backend onfailure:", $1.set[0].v.sval);
+ onfailstatement {
+ psmsg ("backend onfail:", $1.set[0].v.sval);
$$ = $1;
}
|
@@ -764,23 +933,23 @@ serveraddress:
}
;
-onsuccessstatement:
- ONSUCCESS
+onstartstatement:
+ ONSTART
commandline
semicol {
- psmsg ("onsuccess statement:", laststr);
+ psmsg ("onstart statement:", laststr);
$$.n = 1;
$$.set = xmalloc (sizeof (Confset));
- $$.set[0].cf = cf_onsuccspec;
+ $$.set[0].cf = cf_onstartspec;
$$.set[0].v.sval = xstrdup (laststr);
}
;
-onfailurestatement:
- ONFAILURE
+onfailstatement:
+ ONFAIL
commandline
semicol {
- psmsg ("onfailure statement:", laststr);
+ psmsg ("onfail statement:", laststr);
$$.n = 1;
$$.set = xmalloc (sizeof (Confset));
$$.set[0].cf = cf_onfailspec;
@@ -788,6 +957,18 @@ onfailurestatement:
}
;
+onendstatement:
+ ONEND
+ commandline
+ semicol {
+ psmsg ("onend statement:", laststr);
+ $$.n = 1;
+ $$.set = xmalloc (sizeof (Confset));
+ $$.set[0].cf = cf_onendspec;
+ $$.set[0].v.sval = xstrdup (laststr);
+ }
+;
+
dumptrafficstatement:
TRAFFICLOG
filename
@@ -841,7 +1022,7 @@ stickycookiestatement:
$$.set[0].v.sval = xstrdup (laststr);
}
;
-
+
cookiespecifier:
cookie_expected
STRING {
@@ -1011,3 +1192,13 @@ type_expected: {
yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)";
}
;
+
+ipfilters_expected: {
+ yyerrmsg = "IP filter(s) expected";
+}
+;
+
+useraccount_expected: {
+ yyerrmsg = "username expected";
+}
+;
diff --git a/src/restart.c b/src/restart.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int restart (int ac, char **av) {
@@ -8,6 +13,8 @@ int restart (int ac, char **av) {
if (!stop_daemon (ac, av))
error ("Daemon stop failed, restart failure");
+ sleep (1);
+
/* Now run 'start' */
if (!serve (ac, av))
error ("Daemon start failed, restart failure");
diff --git a/src/runservice.c b/src/runservice.c
@@ -1,13 +1,27 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void runservice () {
int pid;
+ int first_serving = 1;
msg ("Service %s (on port %d): STARTING",
activeservice->name, activeservice->port);
/* Allocate the shared mem segment. */
alloc_reporter(activeservice, 1);
+
+ /* Load any allow- or deny filter files if appropriate. */
+ if (ipf_loadfile (activeservice->allowfile, &(activeservice->allowchain),
+ &(activeservice->nallowchain)))
+ error ("Error in allow file");
+ if (ipf_loadfile (activeservice->denyfile, &(activeservice->denychain),
+ &(activeservice->ndenychain)))
+ error ("Error in deny file");
/* Go into the background. */
if ( (pid = fork()) < 0 ) {
@@ -19,34 +33,29 @@ void runservice () {
msg ("Service %s: detached as PID %d",
activeservice->name, pid);
return;
- } else {
- /* Child branch */
- set_program_title ("service %s listening", activeservice->name);
- close (0);
- close (1);
- close (2);
- daemonized++;
- if ( (open ("/dev/null", O_RDONLY) < 0) ||
- (open ("/dev/null", O_WRONLY) < 0) ||
- (open ("/dev/null", O_WRONLY) < 0) )
- error ("Service %s: "
- "failed to reopen stdin/out/err on /dev/null",
- activeservice->name);
- if (setsid() < 0)
- error ("Service %s: failed to become seesion leader",
- activeservice->name);
+ }
+
+ /* Child branch */
+ set_program_title ("Service %s: listening", activeservice->name);
+ close (0);
+ close (1);
+ close (2);
+ daemonized++;
+ if ( (open ("/dev/null", O_RDONLY) < 0) ||
+ (open ("/dev/null", O_WRONLY) < 0) ||
+ (open ("/dev/null", O_WRONLY) < 0) )
+ error ("Service %s: "
+ "failed to reopen stdin/out/err on /dev/null",
+ activeservice->name);
+ if (setsid() < 0)
+ error ("Service %s: failed to become seesion leader",
+ activeservice->name);
- /* Store our pid, so that 'crossroads stop' may kill us. */
- lock_reporter();
- servicereport->pid = getpid();
- unlock_reporter();
-
- /* Promote verbosity. */
- flag_verbose = activeservice->verbosity;
+ /* Promote verbosity. */
+ flag_verbose = activeservice->verbosity;
- /* We've forked for the first time */
- program_stage = stage_waiting;
- }
+ /* We've forked for the first time */
+ program_stage = stage_waiting;
/* In 'forever' mode, we create a server-side socket and ask tcpserve()
* to service it. tcpserve() will return to us if there are no back
@@ -55,7 +64,7 @@ void runservice () {
* Of course we only start listening if we have back ends at all...
*/
while (1) {
- if (backend_available()) {
+ if (backend_count()) {
/* Create the socket, bind to port. */
if ( (listen_sock = make_socket (activeservice->port,
activeservice->bind)) < 0 ) {
@@ -77,6 +86,16 @@ void runservice () {
msg ("Service %s: server-side network socket: %d",
activeservice->name, listen_sock);
+ /* Zero out the stats for first usage. */
+ if (first_serving) {
+ first_serving = 0;
+ memset (servicereport, 0, sizeof(Servicereport));
+ /* Store our pid, so that 'crossroads stop' may kill us. */
+ lock_reporter();
+ servicereport->pid = getpid();
+ unlock_reporter();
+ }
+
/* Start serving the port! */
tcpserve (listen_sock);
diff --git a/src/sample.conf b/src/sample.conf
@@ -1,183 +0,0 @@
-/*
- * Sample configuration for crossroads
- * Default location of this is /etc/crossroads.conf, unless overruled by -c.
- */
-
-# Empty lines are allowed, # shell-style comment too.
-// And C++ style comment as well.
-
-/* Service www: TCP activity on port 8000 gets distributed
- * over a couple of webserver back ends.
- */
-service www {
-
- /* Port on which we'll listen in this service: required. */
- port 8000;
-
- /* What IP address should this service listen? Default is 'any'.
- * Alternatively you can state an explicit IP address, such as
- * 127.0.0.1; that would bind the service only to 'localhost'. */
- bindto any;
-
- /* Verbose reporting or not. Default is off. */
- verbosity on;
-
- /* Service type: 'http' or 'any'. With 'http' you can make sessions
- * "stick" to a once selected back end. If you don't need stickiness,
- * use 'any' which is the default. */
- type any;
-
- /* Dispatching mode, or: How to select a back end for an incoming
- * request. Possible values:
- * roundrobin: just the next back end in line
- * random: like roundrobin, but at random to make things more
- * confusing. Probably only good for testing.
- * bysize: The backend that transferred the least nr of bytes
- * is the next in line. As a modifier you can say e.g.
- * bysize over 10, meaning that the 10 last connections will
- * be used to compute the transfer size, instead of all
- * transfers.
- * byduration: The backend that was active for the shortest time
- * is the next in line. As a modifier you can say e.g.
- * byduration of 10 to compute over the last 10 connections.
- * byconnections: The back end with the least number of active
- * connections is the next in line.
- * byorder: The first available back end is always taken.
- */
- dispatchmode byduration over 5;
-
- /* Interval at which we'll check whether a temporarily unavailable
- * backend has woken up.
- */
- revivinginterval 5;
-
- /* TCP backlog of connections. Default is 0 (no backlog, one
- * connection may be active).
- */
- backlog 5;
-
- /* For status reporting: a shared memory key. Default is the same
- * as the port number, OR-ed by a magic number.
- */
- shmkey 8000;
-
- /* This controls when crossroads should consider a connection as
- * finished even when the TCP sockets weren't closed. This is to
- * avoid hanging connections that don't do anything. NOTE THAT when
- * crossroads cuts off a connection due to timeout exceed, this is
- * not marked as a failure, but as a success. Default is 0: no timeout.
- */
- connectiontimeout 300;
-
- /* The max number of allowed client connections. When present, connections
- * won't be accepted if the max is about to be exceeded. When
- * absent, all connections will be accepted, which might be misused
- * for a DOS attack.
- */
- maxconnections 300;
-
- /* Now let's define a couple of back ends. Number 1: */
- backend www_backend_1 {
- /* The server and its port, the minimum configuration. */
- server httpserver1;
- port 9010;
-
- /* The 'decay' of usage data of this back end. Only relevant
- * when the whole service has 'dispatchmode bysize' or
- * 'byduration'. The number is a percentage by which the usage
- * parameter is decreased upon each connection of an other back
- * end.
- */
- decay 10;
-
- /* To see what's happening in /var/log/messages: */
- verbosity on;
- }
-
- /* The second one. For this back end we want to see the response
- * times, so we specify a throughput log. */
- backend www_backend_2 {
- /* Server and port. You can use a shorthand for both host and port: */
- server httpserver2:9011;
-
- /* Verbosity of reporting when this back end is active */
- verbosity on;
-
- /* Decay */
- decay 10;
-
- /* Performance related */
- throughputlog /tmp/backend.2.perflog;
-
- /* Event triggers for system commands upon succesful activation
- * and upon failure.
- */
- onsuccess echo 'success on backend 2' | mail root;
- onfailure echo 'failure on backend 2' | mail root;
- }
-
- /* And yet another one.. this time we will dump the traffic
- * to a trace file. Furthermore we don't want more than 10 concurrent
- * connections here. Note that there's also a total maxconnections for the
- * whole service.
- */
- backend www_backend_3 {
- server httpserver3:9000;
- verbosity on;
- decay 10;
- trafficlog /tmp/backend.3.log;
- maxconnections 10;
- }
-}
-
-/* Another example: SSH login failover. */
-service ssh {
- port 2222; // Incoming port
- type any; // Generic TCP service
- verbosity on; // Let's be verbose
- shmkey 2222; // Shared memory related
- connectiontimeout 30; // Auto-logout after 30 secs
- dispatchmode bysize over 1; // Least bytes = least active,
- // only last connection matters
- maxconnections 1; // 1 concurrent login
-
- backend ssh_1 { // First back end
- server sshserver1:22;
- verbosity on;
- onfailure echo 'SSH failure on sshserver1' | mail root;
- }
-
- backend ssh_2 { // Second back end
- server sshserver2:22;
- verbosity on;
- onfailure echo 'SSH failure on sshserver2' | mail root;
- }
-}
-
-/* Yet another example: sticky HTTP for an application that's hosted on
- * 3 back ends, but the each of the apps isn't session-aware of the others.
- * As for the balancing we'll simply round-robin it.
- */
-service balancedapp {
- port 8001;
- type stickyhttp;
- backend app1 {
- server appserver1:8080;
- /* If 'BalancerID=a' is already in the browser's request, then
- * the request automatically goes to this backend */
- stickycookie BalancerID=a;
- /* The server return is enriched with the following cookie
- * instruction (note that this matches 'stickycookie' above) */
- insertcookie "BalancerID=a; Path=/";
- }
- backend app2 {
- server appserver2:8080;
- stickycookie BalancerID=b;
- insertcookie "BalancerID=b; Path=/";
- }
- backend app3 {
- server appserver3:8080;
- stickycookie BalancerID=c;
- insertcookie "BalancerID=c; Path=/";
- }
-}
diff --git a/src/sampleconf.c b/src/sampleconf.c
@@ -1,7 +0,0 @@
-#include "crossroads.h"
-#include "sampleconf.h"
-
-int sample_conf (int ac, char **av) {
- printf ("%s\n", SAMPLECONF);
- return (1);
-}
diff --git a/src/serve.c b/src/serve.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int serve (int ac, char **av) {
diff --git a/src/setprogramtitle.c b/src/setprogramtitle.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void set_program_title (char const *fmt, ...) {
diff --git a/src/showservices.c b/src/showservices.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int show_services (int ac, char **av) {
@@ -6,8 +11,12 @@ int show_services (int ac, char **av) {
for (i = 0; i < nservice; i++) {
printf ("Service %s (on port %d) %d backends\n",
service[i].name, service[i].port, service[i].nbackend);
+ if (service[i].allowfile)
+ printf (" Allow file: %s\n", service[i].allowfile);
+ if (service[i].denyfile)
+ printf (" Deny file: %s\n", service[i].denyfile);
for (j = 0; j < service[i].nbackend; j++)
- printf (" Backend %s (on host %s, port %d)\n",
+ printf (" Backend %s (%s:%d)\n",
service[i].backend[j].name,
service[i].backend[j].server,
service[i].backend[j].port);
diff --git a/src/showstatus.c b/src/showstatus.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
static char *timestr (double nsec) {
@@ -17,7 +22,7 @@ static char *timestr (double nsec) {
buf = xstrcat (buf, tmp);
free (tmp);
}
- tmp = str_printf ("%.2f", nsec);
+ tmp = str_printf ("%.2fs", nsec);
buf = xstrcat (buf, tmp);
free (tmp);
@@ -28,14 +33,23 @@ static char *bytestr (unsigned long long nbytes) {
static char *buf;
free (buf);
- buf = 0;
-
- if (nbytes > 1024*1024*1024)
- buf = str_printf ("%.2fGb", (double) nbytes / (1024*1024*1024));
- if (nbytes > 1024*1024)
- buf = str_printf ("%.2fMb", (double) nbytes / (1024*1024));
+
+ if (nbytes > (double)1024*1024*1024*1024)
+ buf = str_printf ("%.2fTb",
+ ((double) nbytes) /
+ ((double) 1024*1024*1024*1024));
+ else if (nbytes > 1024*1024*1024)
+ buf = str_printf ("%.2fGb",
+ ((double) nbytes) /
+ ((double) 1024*1024*1024));
+ else if (nbytes > 1024*1024)
+ buf = str_printf ("%.2fMb",
+ ((double) nbytes) /
+ ((double) 1024*1024));
else if (nbytes > 1024)
- buf = str_printf ("%.2fKb", (double) nbytes / 1024);
+ buf = str_printf ("%.2fKb",
+ ((double) nbytes) /
+ (double) 1024);
else
buf = str_printf ("%llub", nbytes);
@@ -51,12 +65,12 @@ int show_status (int ac, char **av) {
alloc_reporter (service + i, 0);
msg ("Reporting service %s (pid = %d)",
service[i].name, servicereport->pid);
- printf ("Service : %s, %d connections, last backend %d\n",
+ printf ("Service : %s, %d live connections, last backend %d\n",
service[i].name, servicereport->nclients,
servicereport->last_backend);
for (j = 0; j < service[i].nbackend; j++) {
- printf (" Backend %2d : %s is %s, %u connections\n"
- " Stats : %lu failed in %lu connections,",
+ printf (" Backend %2d : %s is %s, %u live connections\n"
+ " Stats : %lu failures out of %lu connections",
j,
service[i].backend[j].name,
state_to_string (servicereport->backendstate[j].avail),
@@ -64,13 +78,14 @@ int show_status (int ac, char **av) {
servicereport->backendstate[j].failures,
servicereport->backendstate[j].totuses);
if (service[i].type == type_http)
- printf (" %lu sessions,",
+ printf (", %lu sessions,",
servicereport->backendstate[j].sessions);
- printf (" usage %s, %s",
+ printf ("\n"
+ " usage %s, %s",
timestr (servicereport->backendstate[j].nsec),
bytestr (servicereport->backendstate[j].nbytes));
if (service[i].dispatchover)
- printf (" avg %s, %s",
+ printf (", avg %s, %s",
timestr(servicereport->backendstate[j].avg_nsec),
bytestr(servicereport->backendstate[j].avg_nbytes));
putchar ('\n');
diff --git a/src/stagetostring.c b/src/stagetostring.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *stage_to_string (Programstage stage) {
diff --git a/src/statetostring.c b/src/statetostring.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *state_to_string (Backendavail avail) {
diff --git a/src/stopdaemon.c b/src/stopdaemon.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int stop_daemon (int ac, char **av) {
diff --git a/src/strcasestr.c b/src/strcasestr.c
@@ -0,0 +1,28 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+#ifndef HAVE_STRCASESTR
+char *strcasestr (char const *big, char const *little) {
+ char *bcopy, *lcopy, *res;
+
+ if (!little || !*little)
+ return (big);
+ if (!big || !*big)
+ return (0);
+
+ bcopy = strupr (xstrdup (big));
+ lcopy = strupr (xstrdup (little));
+ if ( (res = strstr (big, little)) )
+ res = big + (res - bcopy);
+ free (bcopy);
+ free (lcopy);
+ return (res);
+}
+#endif
+
+
+
diff --git a/src/strexpandformat.c b/src/strexpandformat.c
@@ -0,0 +1,105 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+char *fmt_expand (char const *s, int *skip, int target_backend) {
+ static char buf[80];
+ char *ret;
+
+ /* Expansion table:
+ * %a - availability of current back end (if any), 0 not available,
+ * %1a = first back end etc.
+ * %b - name of current back end (if any), %1b = first back end etc
+ * %e - seconds since epoch
+ * %n - nr of clients of current back end, %1n = first back end etc.
+ * %r - client IP
+ * %s - name of service
+ * %t, %T - timestamp of local or GMT time
+ * %v - Crossroads version
+ * %w - weight of current back end, %1w = first back end etc.
+ */
+
+ *skip = 1;
+ switch (*s) {
+
+ case 'a':
+ if (backend_available (target_backend))
+ return ("1");
+ return ("0");
+
+ case 'b':
+ if (target_backend >= 0 && target_backend < activeservice->nbackend)
+ return (activeservice->backend[target_backend].name);
+ return ("noname");
+
+ case 'e':
+ snprintf (buf, sizeof(buf) - 1, "%u", (unsigned) time(0));
+ return (buf);
+
+ case 'n':
+ if (target_backend >= 0 && target_backend < activeservice->nbackend) {
+ snprintf (buf, sizeof(buf) - 1, "%u",
+ (unsigned)
+ servicereport->backendstate[target_backend].nclients);
+ return (buf);
+ }
+ return ("0");
+
+ case 'r':
+ if (client_ip && *client_ip)
+ return (client_ip);
+ return ("0.0.0.0");
+
+ case 's':
+ return (activeservice->name);
+
+ case 't':
+ return (ansistamp (tm_localtime));
+
+ case 'T':
+ return (ansistamp (tm_gmtime));
+
+ case 'v':
+ return (VER);
+
+ case 'w':
+ if (target_backend >= 0 && target_backend < activeservice->nbackend) {
+ snprintf (buf, sizeof(buf) - 1, "%u",
+ (unsigned)
+ activeservice->backend[target_backend].weight);
+ return (buf);
+ }
+ return ("0");
+
+ default:
+ if (isdigit (*s)) {
+ ret = fmt_expand (s + 1, skip, *s - '0' - 1);
+ *skip += 1;
+ return (ret);
+ } else {
+ *buf = *(s + 1);
+ *(buf + 1) = 0;
+ return (buf);
+ }
+ }
+}
+
+char *str_expand_format (char const *h) {
+ char *ret = 0;
+ char const *cp;
+ int skip;
+
+ for (cp = h; cp && *cp; cp++) {
+ if (*cp == '%') {
+ ret = xstrcat (ret, fmt_expand (cp + 1, &skip, current_backend));
+ cp += skip;
+ } else {
+ ret = xstrcatch (ret, *cp);
+ }
+ }
+
+ return (ret);
+}
diff --git a/src/stringtostate.c b/src/stringtostate.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
Backendavail string_to_state (char const *str) {
diff --git a/src/strlcat.c b/src/strlcat.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#ifndef HAVE_STRLCAT
#include "crossroads.h"
diff --git a/src/strprintf.c b/src/strprintf.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *str_printf (char const *fmt, ...) {
diff --git a/src/strvprintf.c b/src/strvprintf.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
#define STR_BLOCK 512
diff --git a/src/sysrun.c b/src/sysrun.c
@@ -1,8 +1,22 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void sysrun (char const *cmd) {
+ char *expanded;
+ int ret;
+
if (!cmd)
return;
- msg ("Running command: '%s'", cmd);
- system (cmd);
+ expanded = str_expand_format (cmd);
+ msg ("Service %s: running command: '%s'", activeservice->name, expanded);
+ uid_assume();
+ if ( (ret = system (expanded)) )
+ warning ("Service %s: command '%s' returned %d",
+ activeservice->name, expanded, ret);
+ uid_restore();
+ free (expanded);
}
diff --git a/src/tcpserve.c b/src/tcpserve.c
@@ -1,8 +1,13 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void tcpserve (int server_sock) {
fd_set set;
- int backend_sock, new, size, i, pid;
+ int backend_sock, new, size, i, pid, backend_selected;
struct sockaddr_in clientname;
static int wakeup_started = 0;
@@ -11,12 +16,14 @@ void tcpserve (int server_sock) {
/* Interruption signals */
for (i = 0; relevant_sigs[i]; i++)
signal (relevant_sigs[i], interrupt);
+ /* Child termination is ignored. */
+ signal (SIGCHLD, SIG_IGN);
/* Set wakeup handler for the wakeup calls. */
if (activeservice->rev_interval) {
if ( (pid = fork()) < 0 )
error ("Fork failed: %s", strerror(errno));
else if (!pid) {
- set_program_title ("wakeup for %s", activeservice->name);
+ set_program_title ("Service %s: wakeup", activeservice->name);
wakeup_handler();
} else {
msg ("Service %s: started wakeup handler at pid %d",
@@ -32,10 +39,7 @@ void tcpserve (int server_sock) {
if (listen (server_sock, activeservice->backlog + 1) < 0)
error ("Service %s: failed to listen to server_socket: %s",
activeservice->name, strerror(errno));
-
- /* Avoid zombies. */
- signal (SIGCHLD, SIG_IGN);
-
+
/* We're a service now. Never return, never exit
* (unless something is REALLY wrong).
*/
@@ -43,9 +47,11 @@ void tcpserve (int server_sock) {
/* Block until we get a connection. */
FD_ZERO (&set);
FD_SET (server_sock, &set);
- if (select (FD_SETSIZE, &set, 0, 0, 0) < 0)
- error ("Service %s: error while waiting for activity: %d (%s)",
- activeservice->name, errno, strerror(errno));
+ if (select (FD_SETSIZE, &set, 0, 0, 0) < 0) {
+ msg ("Service %s: interrupt while waiting for activity: %d (%s)",
+ activeservice->name, errno, strerror(errno));
+ continue;
+ }
/* Accept the client-side connection. */
size = sizeof(clientname);
@@ -58,6 +64,18 @@ void tcpserve (int server_sock) {
client_ip = inet_ntoa (clientname.sin_addr);
msg ("Service %s: connection from %s, socket %d",
activeservice->name, client_ip, new);
+ if (ipf_denied ()) {
+ warning ("Service %s: %s matches deny list, "
+ "terminating connection", activeservice->name, client_ip);
+ close (new);
+ continue;
+ }
+ if (!ipf_allowed ()) {
+ warning ("Service %s: %s failes to match allow list, "
+ "terminating connection", activeservice->name, client_ip);
+ close (new);
+ continue;
+ }
/* Backend yet to be defined. */
current_backend = -1;
@@ -75,7 +93,7 @@ void tcpserve (int server_sock) {
* runservice() may close the listener socket, sleep, and
* create a new one.
*/
- if (! backend_available()) {
+ if (! backend_count()) {
warning ("Service %s: no back ends available",
activeservice->name);
close (new);
@@ -83,7 +101,10 @@ void tcpserve (int server_sock) {
}
/* Retry back ends until we succeed. */
- while (1) {
+ backend_selected = 0;
+ while (!backend_selected) {
+ msg ("Service %s: About to choose a back end",
+ activeservice->name);
choose_backend();
if (current_backend < 0) {
/* No back ends available now. NOTE: we return so that
@@ -92,40 +113,39 @@ void tcpserve (int server_sock) {
return;
}
- /* Show what's happening. */
+ /* Connect to the backend. If this fails then we'll sleep
+ * and re-enter the backend selection loop instead of returning.
+ * In the loop we may need to decide to return after all. */
msg ("Service %s: trying back end %s, port %d",
activeservice->name,
activeservice->backend[current_backend].server,
activeservice->backend[current_backend].port);
-
- /* Connect to the backend. If this fails then we'll sleep
- * and re-enter the while(1) loop instead of returning.
- * In the loop we may need to decide to return after all. */
if ( (backend_sock = backend_connect()) < 0 ) {
- sysrun (activeservice->backend[current_backend].onfailure);
- warning ("Service %s: failed to connect to server %s:%d",
- activeservice->name,
+ warning ("Service %s: failed to connect to server %s:%d "
+ "(ret %d)", activeservice->name,
activeservice->backend[current_backend].server,
- activeservice->backend[current_backend].port);
+ activeservice->backend[current_backend].port,
+ backend_sock);
continue;
}
/* Got a live one! */
+ backend_selected++;
if (fork_tcp_servicer (current_backend)) {
/* We're the parent branch here. Close sockets so that
* we don't run out of file descriptors, and loop into
* the next select/accept run. */
close (new);
close (backend_sock);
- break;
+ } else {
+ /* Child branch: piggyback to and fro.
+ * This one never returns. */
+ incr_client_count();
+ log_activity_start ();
+ flag_verbose =
+ activeservice->backend[current_backend].verbosity;
+ copysockets (new, backend_sock);
}
-
- /* Child branch: piggyback to and fro.
- * This one never returns. */
- log_activity_start ();
- incr_client_count ();
- flag_verbose = activeservice->backend[current_backend].verbosity;
- copysockets (new, backend_sock);
}
}
}
diff --git a/src/tellservice.c b/src/tellservice.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
int tell_service (int ac, char **av) {
diff --git a/src/thruputlog.c b/src/thruputlog.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void thruputlog (unsigned char const *buf, int len, CopyDirection dir) {
diff --git a/src/trafficlog.c b/src/trafficlog.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void trafficlog (unsigned char const *buf, int len, CopyDirection dir) {
diff --git a/src/uidassume.c b/src/uidassume.c
@@ -0,0 +1,27 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+void uid_assume() {
+ uid_set = 0;
+ gid_set = 0;
+
+ if (activeservice->uid) {
+ if (activeservice->gid) {
+ gid_org = getgid();
+ if (setegid (activeservice->gid))
+ error ("Service %s: cannot set effective gid to %d: %s",
+ activeservice->name, activeservice->gid,
+ strerror(errno));
+ gid_set++;
+ }
+ uid_org = getuid();
+ if (seteuid (activeservice->uid))
+ error ("Service %s: cannot set effective uid to %d: %s",
+ activeservice->name, activeservice->uid, strerror(errno));
+ uid_set++;
+ }
+}
diff --git a/src/uidrestore.c b/src/uidrestore.c
@@ -0,0 +1,13 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
+#include "crossroads.h"
+
+void uid_restore () {
+ if (uid_set)
+ seteuid (uid_org);
+ if (gid_set)
+ setegid (gid_org);
+}
diff --git a/src/unlockreporter.c b/src/unlockreporter.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void unlock_reporter() {
diff --git a/src/usage.c b/src/usage.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
#include "usage.h"
diff --git a/src/vsyslog.c b/src/vsyslog.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
#ifndef HAVE_VSYSLOG
diff --git a/src/wakeuphandler.c b/src/wakeuphandler.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void wakeup_handler () {
@@ -32,14 +37,14 @@ void wakeup_handler () {
/* Try to TCP connect */
sock = backend_connect();
- close (sock);
-
+
/* Set state accordingly */
- if (sock >= 0)
+ if (sock >= 0) {
+ close (sock);
warning ("Backend %s of service %s has woken up",
activeservice->backend[current_backend].name,
activeservice->name);
-
+ }
lock_reporter();
servicereport->backendstate[current_backend].avail =
sock >= 0 ? st_available : st_unavailable;
diff --git a/src/warning.c b/src/warning.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void warning (char const *fmt, ...) {
diff --git a/src/writelog.c b/src/writelog.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void writelog (int prio, char const *fmt, ...) {
diff --git a/src/xmalloc.c b/src/xmalloc.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void *xmalloc (unsigned sz) {
diff --git a/src/xrealloc.c b/src/xrealloc.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
void *xrealloc (void *block, unsigned sz) {
diff --git a/src/xstrcat.c b/src/xstrcat.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *xstrcat (char *what, char const *rest) {
diff --git a/src/xstrcatch.c b/src/xstrcatch.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *xstrcatch (char *what, char ch) {
diff --git a/src/xstrdup.c b/src/xstrdup.c
@@ -1,3 +1,8 @@
+/*************************************************************************
+ * This file is part of Crosroads 1.23, a load balancer and fail over
+ * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL.
+ * Visit http://crossroads.e-tunity.com for information.
+ *************************************************************************/
#include "crossroads.h"
char *xstrdup (char const *what) {
diff --git a/tools/patch-header b/tools/patch-header
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+
+use strict;
+
+sub diff ($$) {
+ my ($a, $b) = @_;
+
+ open (my $fa, $a) or die ("Cannot read $a: $!\n");
+ open (my $fb, $b) or die ("Cannot read $b: $!\n");
+
+ while (1) {
+ my $la = <$fa>;
+ my $lb = <$fb>;
+
+ if (defined ($la)) {
+ return (1)
+ if ( (defined ($lb) and $la ne $lb) or
+ (! defined ($lb)) );
+ } else {
+ return (1)
+ if (defined ($lb));
+ return (0);
+ }
+ }
+}
+
+die ("Usage: patch-header header-template version file(s)\n")
+ if ($#ARGV < 2);
+my $hdrfile = shift (@ARGV);
+my $ver = shift (@ARGV);
+my ($header, $firstline, $lastline);
+open (my $if, $hdrfile)
+ or die ("Can't read header file $hdrfile: $!\n");
+while (my $line = <$if>) {
+ chomp ($line);
+
+ $line =~ s/__VER__/$ver/g;
+ $header .= "$line\n";
+
+ $firstline = $line if ($firstline eq '');
+ $lastline = $line if ($line ne '');
+}
+close ($if);
+
+for my $src (@ARGV) {
+ my $dst = "$src.new";
+ open (my $of, ">$dst")
+ or die ("Can't write $dst: $!\n");
+ print $of ($header);
+
+ open (my $if, $src)
+ or die ("Can't read $src: $!\n");
+ my $state = 0;
+ while (my $line = <$if>) {
+ chomp ($line);
+ # print ("Line: [$line], first: [$firstline], state: [$state]\n");
+ if ($state == 2) {
+ print $of ("$line\n");
+ next;
+ }
+ if ($state == 0 and $line eq $firstline) {
+ $state = 1;
+ next;
+ }
+ if ($state == 1 and $line eq $lastline) {
+ $state = 2;
+ next;
+ }
+ if ($state == 0) {
+ print $of ("$line\n");
+ next;
+ }
+ }
+ close ($of);
+ close ($if);
+
+ if (diff ($src, $dst)) {
+ unlink ($src)
+ or die ("Cannot unlink $src: $!\n");
+ rename ($dst, $src)
+ or die ("Cannot rename $dst to $src: $!\n");
+ print ("patch-header: $ver $src\n");
+ } else {
+ unlink ($dst)
+ or die ("Cannot unlink $dst: $!\n");
+ }
+}