crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs

commit 85b77ed0114b5a6cdae2ee91a9209edeb03b6009
parent 00b9a457e57ed25bd9a9edcc5bc4c9d8bb0db5d4
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:15:07 +0100

1.23

Diffstat:
MChangeLog | 40++++++++++++++++++++++++++++++++++++++++
MMakefile | 8+++++---
Adoc/conf/addclientheader.yo | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/conf/allow.yo | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/conf/backlog.yo | 12++++++++++++
Adoc/conf/bindto.yo | 12++++++++++++
Adoc/conf/connectiontimeout.yo | 11+++++++++++
Adoc/conf/decay.yo | 18++++++++++++++++++
Adoc/conf/dispatchmode.yo | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/conf/maxconnections.yo | 12++++++++++++
Adoc/conf/onhooks.yo | 25+++++++++++++++++++++++++
Adoc/conf/port.yo | 8++++++++
Adoc/conf/revivinginterval.yo | 18++++++++++++++++++
Adoc/conf/server.yo | 10++++++++++
Adoc/conf/shmkey.yo | 17+++++++++++++++++
Adoc/conf/stickycookie.yo | 35+++++++++++++++++++++++++++++++++++
Adoc/conf/trafficlog.yo | 17+++++++++++++++++
Adoc/conf/type.yo | 18++++++++++++++++++
Adoc/conf/useraccount.yo | 15+++++++++++++++
Adoc/conf/verbose-backend.yo | 10++++++++++
Adoc/conf/verbose.yo | 15+++++++++++++++
Adoc/conf/weight.yo | 15+++++++++++++++
Mdoc/config.yo | 526+++++--------------------------------------------------------------------------
Mdoc/crossroads.html | 1639+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdoc/crossroads.man | 1331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdoc/crossroads.pdf | 0
Adoc/formattable.yo | 23+++++++++++++++++++++++
Mdoc/intro.yo | 14++++++++++++++
Mdoc/tips.yo | 575++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdoc/using.yo | 15+++++++++++++--
Metc/Makefile.def | 2+-
Aetc/dispatcher-roundrobin | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aetc/hdr.template | 5+++++
Aetc/rdp-helper | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Makefile | 11++++-------
Msrc/allocreporter.c | 9+++++----
Msrc/ansistamp.c | 5+++++
Msrc/backendavailable.c | 43++++++++++++++++++++-----------------------
Msrc/backendconnect.c | 42+++++++++++++++++++++++++++++++++---------
Asrc/backendcount.c | 16++++++++++++++++
Msrc/choosebackend.c | 57++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/configtest.c | 5+++++
Msrc/copysockets.c | 5+++++
Msrc/createcommandlinespace.c | 5+++++
Msrc/crossroads.h | 58+++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/deallocreporter.c | 5+++++
Msrc/decrclientcount.c | 28++++++++++++++++++++++++----
Msrc/error.c | 5+++++
Msrc/forktcpservicer.c | 11+++++++++--
Msrc/httpcopy.c | 5+++++
Msrc/httperror.c | 6++++++
Dsrc/httpexpandheader.c | 41-----------------------------------------
Msrc/httpheaderaddheader.c | 7++++++-
Msrc/httpheaderappendheader.c | 7++++++-
Msrc/httpheaderconnectiontype.c | 9+++++++--
Msrc/httpheaderfree.c | 5+++++
Msrc/httpheaderhascookie.c | 5+++++
Msrc/httpheaderhttpver.c | 5+++++
Msrc/httpheadernew.c | 5+++++
Msrc/httpheaderread.c | 5+++++
Msrc/httpheaderremoveheader.c | 5+++++
Msrc/httpheadersetheader.c | 7++++++-
Msrc/httpheaderval.c | 5+++++
Msrc/httpheaderwrite.c | 5+++++
Msrc/httpinsertheader.c | 5+++++
Msrc/httpserve.c | 8+++++++-
Msrc/httpserversocket.c | 16+++++++++++++++-
Msrc/httpwrite.c | 5+++++
Msrc/incrclientcount.c | 31+++++++++++++++++++++++++++----
Msrc/initsockaddr.c | 5+++++
Msrc/interrupt.c | 30++++++++++++++++++++++++++++++
Asrc/ipfaddallow.c | 17+++++++++++++++++
Asrc/ipfadddeny.c | 17+++++++++++++++++
Asrc/ipfallowed.c | 22++++++++++++++++++++++
Asrc/ipfdenied.c | 22++++++++++++++++++++++
Asrc/ipfloadfile.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ipfmatch.c | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/ipfparse.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ishexdigit.c | 5+++++
Msrc/isspace.c | 5+++++
Msrc/lexer.c | 1110+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/lexer.l | 658+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/lockreporter.c | 5+++++
Msrc/logactivityany.c | 5+++++
Msrc/logactivitycontinuation.c | 5+++++
Msrc/logactivityend.c | 5+++++
Msrc/logactivitystart.c | 5+++++
Msrc/main.c | 9++++++++-
Msrc/makesocket.c | 5+++++
Msrc/markactivity.c | 14++++++++------
Msrc/msg.c | 5+++++
Msrc/msgdumpbuf.c | 5+++++
Msrc/netbuffer.c | 5+++++
Msrc/netbufread.c | 5+++++
Msrc/netcopy.c | 7+++++++
Msrc/netread.c | 7+++++++
Msrc/netwrite.c | 5+++++
Msrc/parser.c | 956++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/parser.h | 12++++++++++--
Msrc/parser.y | 291+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/restart.c | 7+++++++
Msrc/runservice.c | 73++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Dsrc/sample.conf | 183-------------------------------------------------------------------------------
Dsrc/sampleconf.c | 7-------
Msrc/serve.c | 5+++++
Msrc/setprogramtitle.c | 5+++++
Msrc/showservices.c | 11++++++++++-
Msrc/showstatus.c | 43+++++++++++++++++++++++++++++--------------
Msrc/stagetostring.c | 5+++++
Msrc/statetostring.c | 5+++++
Msrc/stopdaemon.c | 5+++++
Asrc/strcasestr.c | 28++++++++++++++++++++++++++++
Asrc/strexpandformat.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/stringtostate.c | 5+++++
Msrc/strlcat.c | 5+++++
Msrc/strprintf.c | 5+++++
Msrc/strvprintf.c | 5+++++
Msrc/sysrun.c | 18++++++++++++++++--
Msrc/tcpserve.c | 76++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/tellservice.c | 5+++++
Msrc/thruputlog.c | 5+++++
Msrc/trafficlog.c | 5+++++
Asrc/uidassume.c | 27+++++++++++++++++++++++++++
Asrc/uidrestore.c | 13+++++++++++++
Msrc/unlockreporter.c | 5+++++
Msrc/usage.c | 5+++++
Msrc/vsyslog.c | 5+++++
Msrc/wakeuphandler.c | 13+++++++++----
Msrc/warning.c | 5+++++
Msrc/writelog.c | 5+++++
Msrc/xmalloc.c | 5+++++
Msrc/xrealloc.c | 5+++++
Msrc/xstrcat.c | 5+++++
Msrc/xstrcatch.c | 5+++++
Msrc/xstrdup.c | 5+++++
Atools/patch-header | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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'.&nbsp;(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.&nbsp;(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.&nbsp;(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, "&gt;&gt;$log") or return; + print $of (scalar(localtime()), ' ', @_); +} + +# Read the last used back end +# --------------------------- +sub readlast() { + my $ret; + + if (open (my $if, $statefile)) { + $ret = &lt;$if&gt;; + 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, "&gt;$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 &lt;= $#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 &lt; $#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, "&gt;&gt;$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 &lt; $#_; $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 &lt; $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 &lt;= $#weights; $i++) { + if ($bestweight == -1 or $bestweight &gt; $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, &amp;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\" %&gt;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"); + } +}