WASD Hypertext Services - Technical Overview

11 - Conditional Configuration

11.1 - Conditional Keywords
11.2 - Examples
[next] [previous] [contents] [full-page]

Mapping (HTTPD$MAP) and authorization (HTTPD$AUTH) rules may be conditionally applied depending on request, server or other charactersistics. These include

server host name, port
client IP address and host name
browser-accepted content-types, character sets, languages, encodings
browser identification string
scheme ("http:" or "https:", i.e. is it a secure request?)
HTTP method (GET, POST, etc.)
request path, query string, cookie data, refering page
virtual host:port specified in request header
system information (hardware, Alpha/VAX, node name, VMS version, etc.)
system time
random number generation

Conditionals may be nested up to a maximum depth of eight, are not case sensitive and generally match via string comparison (with asterisk and percentage wildcards), although IP addresses will accept a network mask. Conditional and rule text may be indented for clarifying structure. They may be used in the following formats.

  if (condition) then apply rest of line
 
  if (condition)
     then apply one
     or more rules
     up until the corresponding ...
  endif
 
  if (condition)
     then apply one
     or more rules
  else
     apply one or more other rules
     up until the corresponding ...
  endif
 
  if (condition)
     then apply one
     or more rules
  elif (condition)
     apply one or more other rules
     in a sort or case statement
  else
     a possible default rule or rules
     up until the delimiting
  endif

Logical operators are also supported, in conjunction with precedence ordering parentheses, allowing moderately complex compound expressions to be applied in conditionals.

! logical negation
&& logical AND
|| logical OR

There are two more conditional structures that allow previous decisions to be reused. These are unif and the ifif. The first unconditionally includes rules regardless of the current state of execution. The second resumes execution only if the previous if or elif expression was true. The else statement may also be used after an unif to continue only if the previous expression was false. The purpose of these constructs are to allow a single decision statement to include both conditional and unconditional rules.

  if (condition)
     then apply one
     or more rules
  unif
     apply this block of rules
     unconditionally
  ifif
     applied only if the original
     if expression was evaulated as true
  unif
     apply another block of rules
     unconditionally
  else
     and this block of rules
     only if the original if was false
  endif

Conditional syntax is checked a rule load time (either server startup or reload). Basic errors such as unknown keywords and unbalanced parantheses or structure statements will be detected and reported to the corresponding Admin Menu report and to the server process log.

CAUTION

Unless these reports are checked after modifying rule sets syntax errors may result in unexpected mappings or access.


11.1 - Conditional Keywords

The following keywords provide a match between the corresponding request or other value and a string immediately following the delimiting colon. White space or other reserved characters may not be included unless preceded by a backslash. The actual value being used in the conditional matching may be observed using the mapping item of the WATCH facility (18 - WATCH Facility).


Rand: Keyword

At the commencement of each pass a new pseudo-random number is generated (and therefore remains constant during that pass). The rand: conditional is intended to allow some sort of distribution to be built into a set of rules, where each pass (request) generates a different one. The random conditional accepts two parameters, a modulas number, which is used to modulas the base number, and a comparison number, which is compared to the modulas result.

Hence the following conditional rules

  if (rand:3:0)
     do this
  elif (rand:3:1)
     do this
  else
     do this
  endif
would pseudo-randomly generate base numbers of 0, 1, 2 and perform the appropriate conditional block. Over a sufficient number of usages this should produce a relatively even distribution of numbers. If the modulas is specified as less than two (i.e. no distribution factor at all) it defaults to 2 (i.e. a distribution of 50%). Hence the following example should be the equivalent of a coin toss.
  if (rand:)
     heads
  else
     tails
  endif


Time: Keyword

The time: conditional allows server behaviour to change according to the time of day, week, or even year. It compares the supplied parameter to the current system time in one of three ways.

  1. The supplied parameter is in the form "1200-1759", which should be read as "twelve noon to five fifty-nine PM" (i.e. as a time range in minutes, generalized as hhmm-hhmm), where the first is the start time and the second the end time. If the current time is within that range (inclusive) the conditional returns true, otherwise false. If the range doesn't look correct false is always returned.
      if (time:0000-0000)
         it's midnight
      elif (time:0001-1159)
         it's AM
      elif (time:1200-1200)
         it's noon
      else
         it's PM
      endif
    

  2. If the supplied parameter is a single digit it is compared to the VMS day of the week (1-Monday, 2-Tuesday ... 7-Sunday).
      if (time:6 || time:7)
         it's the weekend
      else
         it's the working week
      endif
    

  3. If the supplied string is not in either of the formats described above it is treated as a string match with a VMS comparision time (i.e. yyyy-mm-dd hh-mm-ss.hh).
      if (time:%%%%-05-*)
         it's the month of May
      endif
    


Trnlnm: Keyword

The trnlnm: conditional dynamically translates a logical name and uses the value. One mandatory and up to two optional parameters may be supplied.

  trnlnm:logical-name[;name-table][:string-to-match]

The logical-name must be supplied; without it false is always returned. If just the logical-name is supplied the conditional returns true if the name exists or false if it does not. The default name-table is LNM$FILE_DEV. When the optional name-table is supplied the lookup is confined to that table. If the optional string-to-match is supplied it is matched against the value of the logical and the result returned.


Host Addresses

Host names or addresses can be an alpha-numeric string (if DNS lookup is enabled) or dotted-decimal network address, a slash, then a dotted-decimal mask. For example "131.185.250.0/255.255.255.192". This has a 6 bit subnet. It operates by bitwise-ANDing the client host address with the mask, bitwise-ANDing the network address supplied with the mask, then comparing the two results for equality. Using the above example the host 131.185.250.250 would be accepted, but 131.185.250.50 would be rejected. Equivalent notation for this rule would be "131.185.250.0/26".


11.2 - Examples

The following provides a collection of examples of conditional mapping and authorization rules illustrating the use of wildcard matching, network mask matching and the various formats in which the rules may be blocked.

  1. This first example shows an EXEC mapping rule being applied to a path if the request query string contains the string "example".
      if (query-string:*example*) exec /* /cgi-bin/example/* 
    

  2. In this example a block of mapping statements is processed if the virtual service of the request matches that in the conditional, otherwise the block is skipped. Note the indentation to help clarify the structure.
      if (service:the.host.name:80)
         pass /web/* /dka0/the_host_name_web/*
         pass /graphics/* /dka100/graphics/*
         pass * "404 Resource not found."
      endif
    

  3. This example a series of tests allow a form of case processing where the first to match will be processed and terminate the matching process. In this case if a match does not occur rule processing continues after the endif.
      if (service:the.host.name:80)
         pass /web/* /dka0/the_host_name_web/*
      elif (service:next.host.name:80)
         pass /web/* /dka0/next_host_name_web/*
      elif (service:another.host.name:80)
         pass /web/* /dka0/another_host_name_web/*
      endif
      pass /graphics/* /dka100/graphics/*
      pass * "404 Resource not found."
    

  4. In this (somewhat contrived) example a nested test is used to check (virtual) server name and that the request is being handled via Secure Sockets Layer (SSL) for security. If it is not an informative message is supplied. The else and the quotes are not really required but included here for illustration.
      if (server-name:the.host.name)
         if (scheme:"https")
            pass /secure/* /dka0/the_host_name_web/secure/*
         else
            pass * /dka0/the_host_name_web/secure/only-via-SSL.html
         endif
      endif
    

  5. This would be another way to accomplish a similar objective to example 4. This uses a negation operator to exclude access to successive mappings if not requesting via SSL.
      if (server-name:the.host.name)
         if (!SSL:)
            pass * /web/secure/only-via-SSL.html
         endif
         pass /secure/* /web/secure/*
         pass /other/* /web/other/*
         pass /web/* /web/web/*
         pass * "404 Resource not found."
      endif
    

  6. This example shows the use of a compound conditional using the AND and OR operators. It also illustrates the use of a network mask. It will exclude all access to the specified path unless the request is originating from within a specified network (perhaps an intranet) or via SSL.
      if (path:/sensitive/* && !(remote-addr:131.185.250.0/24 || SSL:))
         pass * 404 "Access denied (SSL only)."
      endif
    

  7. This example illustrates restricting authentication to SSL.
      [[*]]
      ["Your VMS password"=VMS]
      if (!request-scheme:https)
         * r+w,#0
      endif
    

  8. Logical name translation may be used to dynamically alter the flow of rule interpretation.
      if (trnlnm:HTTPD_EXAMPLE)
         pass /* /example/*
      else
         pass /* /*
      endif
    


[next] [previous] [contents] [full-page]