/*****************************************************************************/ #ifdef COMMENTS_WITH_COMMENTS /* MapUrl.c Functions supporting mapping of URLs to VMS file specifications and VMS specifications to URLs, uses the HTTPd rule mapping file. The number of rules it can store is not fixed, but is limited by available memory and the practical considerations of processing large, linear rule databases. Comment lines may be provided by beginning a line with a '#' character. Lines may be continued by ensuring the last character on the line is a '\'. Virtual server syntax is the same as for authentication/authorization rules, [[virtual-host]] (all ports of host) or [[virtual-host:virtual-port]] ( matching specified port of host) for a specific host and [[*]] to resume rules for all virtual hosts. Wildcards --------- A single '*' wildcard matches intervening text up until the first character encountered that matches the character immediately following the wildcard. The rule map /one/*/three/* /first/*/third/* would map the following request path /one/two/three/four into /first/two/third/four but would not match the request path /one/two/and-a-half/three/four Two consecutive ('**') wildcards match intervening text up until the next string that matches the string following the wildcard. The rule map /one/**/three/* /first/*/third/* would map the following request paths /one/two/three/four /one/two/and-a-half/three/four respectively into /first/two/third/four /first/two/and-a-half/third/four Historically (pre v8.1) the template wildcard-matched portions of path strings could only be substituted in the positions of corresponding wildcards in the result string. There can be ocassions where there is not a one-to-one relationship between these wildcard sections in the source (template) and target (result) strings. It is sometimes useful to be able to drop one or more strings from the template and/or change the order of substitution. The use of 'positional wildcards' (for want of an even marginally better term) will allow this. When a wildcard is used in the result string it may optionally be followed by a single apostrophe character (') and a single digit character '1'..'9'. The digit is used to indicate which of the template wildcards is intended (first to last). The following request path /one/two/three/four/five.six would be processed by the rule pass /one/*/three/*/* /*'2/one/*'1/*'3 into the mapped path /four/one/two/five.six Using this notation all template wildcards must be specified 'positionally' :^) This rule is not legal. pass /one/*/three/* /two/*'2/four/* Positional wildcards ONLY APPLY to PASS, MAP, REDIRECT and USER rules. They cannot be used with EXEC, SCRIPT and UXEC rules. Extended File Specification --------------------------- Extended file specifications (required for ODS-5) are supported. A rule is used to flag such paths for ODS-5 tailored processing. Things that are not done to ODS-5 paths; case-changes and character substitution. Things that are done to ODS-5 paths; escaping to and from the extended file specification canonical escape syntax (i.e. '^'). Paths located on ODS-5 volumes need to have the ODS-5 rule SET against them. set /extended/* ODS-5 It is possible to explicitly mark a path as ODS-2 also, although as this is the default it is usually unnecessary. One situation where this might be useful is where all paths are ODS-5 except a small minority, in which case a rule such as the following might be used. set /* ODS-5 set /old-path/* ODS-2 Paths derived using the USER rule are specially processed. The SYSUAF user home directory's structure is determined using a sys$getdvi() to determined it's ACPTYPE. Hence no explicit rule is required for these. Mapping Rules ------------- [[virtual-host:optional-virtual-port]] EXEC template result [setting(s)] [conditional(s)] EXEC (runtime)template result [setting(s)] [conditional(s)] EXEC+ template result [setting(s)] [conditional(s)] FAIL template [conditional(s)] MAP template result [setting(s)] [conditional(s)] PASS template [conditional(s)] PASS template result [setting(s)] [conditional(s)] PROTECT template access-control [setting(s)] [conditional(s)] REDIRECT template result [setting(s)] [conditional(s)] SCRIPT template result [setting(s)] [conditional(s)] SCRIPT (runtime)template result [setting(s)] [conditional(s)] SCRIPT+ template result [setting(s)] [conditional(s)] SET template setting(s) [conditional(s)] USER template result [setting(s)] [conditional(s)] UXEC template result [setting(s)] [conditional(s)] UXEC (runtime)template result [setting(s)] [conditional(s)] UXEC+ template result [setting(s)] [conditional(s)] (Somewhat awkwardly the square brackets both denote optional fields and to literally delimit a conditional. Please do not confuse the two!) Note that as of V8.0 path settings (anything that can be set with a SET rule) can also follow the result string. If the rule applies and any conditional is matched then the settings are applied to the path BEFORE the mapping is done. PASS rules can map to a result comprising an HTTP status code (e.g. 403) with a following textual message (including white-space). Just delimit the result string with double or single quotes, or within curly braces. For example: Similarly, PASS rules can map to a result comprising an 200 HTTP status code with a following dollar symbol and CLI command string which is passed to the script processor. PASS /private/stuff/* "403 Can't go in there!" [!ho:my.host.name] PASS /private/stuff/* '403 "/private/stuff/" is off-limits!' [!ho:my.host.name] EXEC rules can be used to map both directories and file types. The former only requires a single, trailing wildcard (example 1). The latter requires two wildcards (examples 2 and 3). The irst to locate the script, the second to map any path. This format can be used to globally map a file type, or confine that mapping to specified part of a tree. EXEC /cgi-bin/* EXEC *.cgi* *.cgi* EXEC /specified/path/*.cgi* /specified/path/*.cgi* PROTECT rules can be used require authorization for for the matching path. It complements the facility available via the WASD_CONFIG_AUTH configuration file, providing a simpler (and to certain extent less capble) authorization schema. See the relevant section in the description to AUTH.C module. General Run-Time Mapping ------------------------ This is distinct from "Run-Time (RTE) Environment" scripting discussed below. It also uses a similar rule construct and historically came much after RTE mappings. Hence the '!' kludge mentioned shortly. The historical way to map script file types (other than .EXE and .COM which are handled implicitly) has been to add [DclScriptRunTime] entries to WASD_CONFIG_GLOBAL. The (very small) disadvantage to using this is the need to completely restart the server to apply changes. Design-wise the better place for these sorts of mappings (file types to run-time engine) is here in WASD_CONFIG_MAP (it also allows /DO=MAP to change these). From v8.2 we can use an RTE-like construct to specify what non-RTE engine should be activated to process the specified script. It can be an executable, a DCL procedure or even verb. Here's how we might activate Perl using these rules: exec /cgi-bin/*.pl* (!perl)/cgi-bin/*.pl* exec /cgi-bin/*.pl* (!$perl_root:[000000]perl.exe)/cgi-bin/*.pl* exec /cgi-bin/*.pl* (!@cgi-bin:[000000]perl.com)/cgi-bin/*.pl* script /this_one* (!$perl_root:[000000]perl.exe)/cgi-bin/this_one.pl* The first uses a CLI verb, the second activates a specified executable and the last executes a DCL procedure (which in this presumably sets up some environment before activating Perl). Note the leading '!'. This is a bit of a kludge but not too onerous. Mapping Logical-Name Content ---------------------------- Generally logical names only provide concealed devices into the file-system for "root"ing some part of the web-space. That is the value of the logical name is only used in RMS (file-system) parsing, etc. There is a single exception. When template and result patterns contain no wildcard(s) a one-to-one mapping if performed. For example pass /this_is_an_example /dka0/example/this.txt and the file content at DKA0:[EXAMPLE]THIS.TXT is returned. With a one-to-one mapping the result string can represent a logical name and the translation of that name used as the mapping element. For a PASS rule the logical name value is used as the VMS result of the mapping (and therefore a VMS file specification). For a REDIRECT rule it should be a URL undertood by redirection. Here are examples: $ DEFINE /SYSTEM /EXEC EXAMPLE_OF DKA0:[EXAMPLE]ONE.TXT $ DEFINE /SYSTEM /EXEC EXAMPLE_GO "/wasd_root/runtime/httpd/" pass /of EXAMPLE_OF redirect /go EXAMPLE_GO Using this capability, the target of a rule may be changed simply by modifying the translated value of a logical name. Note that the mapping rule logical name translation is case-sensitive, and so common use of logical names (all upper-case) means the rule must be all upper case. With all lower or mixed case the rule must specify exact case, as in this example: $ DEFINE /SYSTEM /EXEC "EXAMPLE_go_two" "/wasd_root/src/httpd/" redirect /go_2 EXAMPLE_go_two Run-Time Environment (RTE) Mapping ---------------------------------- Each persistent run-time environment must have it's own mapping environment. This may, or may not, correspond to physically distinct directory areas. It is the mapping rules that identify run-time environments. A parenthesized interprester specification leads the result component of the rule. This is first extracted and then the EXEC rule behaves as normal. Hence the following example shows two such environments each with it's own interpreter. exec /plbin/* (cgi-bin:[000000]perlrte.exe)/ht_root/perl_local/* exec /pybin/* (cgi-bin:[000000]pyrte.exe)/ht_root/python_local/* script /this_one* (cgi-bin:[000000]oneeng.exe)/cgi-bin/this.one* The "/plbin/*" identifies request paths beginning with this string as a hypothetical, persistent Perl run-time interpreter, with the Perl source scripts available to it from HT_ROOT:[PERL_LOCAL]. Similarly with a hypothetical Java Environment. If a request with the following URI is given to the server http://the.host.name/plbin/plsearch/web/doc/ the following components will be derived run-time interpreter ... CGI-BIN:[000000]PERLRT.EXE Perl source script ..... HT_ROOT:[PERL_LOCAL]PLSEARCH.PL request path-info ...... WEB:[DOC] Mapping User Directories ------------------------ The USER rule effectively PASSes the user's SYSUAF default device and directory as the first wildcard substitution in the rule. Accounts that are disusered, captive or have expired passwords are never mapped, they are always "denied access by default". In the same way privileged accounts (those possessing SYSPRV) cannot be mapped this way (see below for a workaround). An example; if the user name DANIEL has the default device USER$DISK: and default directory [DANIEL] the following request path /~daniel/ would be mapped to the result and VMS file specification /user$disk/daniel/www/ USER$DISK:[DANIEL.WWW] using the following rule. USER /~*/* /*/www/ Note the "/www" subdirectory component. It is recommended that user directories always be mapped to a subdirectory of the physcial area. This effectively "sandboxes" Web access to that subdirectory hierarchy, allowing the user privacy elsewhere in the home area. To accomodate request user paths that do not incorporate a trailing delimiter after the username the following redirect may be used to cause the browser to re-request with a more appropriate path. REDIRECT /~* ///*/www/ WASD also "reverse maps" VMS specifications into paths and so requires additional rules to provides these mappings. (Reverse mapping is required during directory listings and error reporting.) In the above case the following rules would actually be required (and in the stated order). USER /~*/* /*/www/ PASS /~*/* /user$disk/*/www/* Where user home directories are spread over multiple devices (physical or concealed logical) a reverse-mapping rule would be required for each. Consider the following situation, where user directories are distributed across these devices (concealed logicals) USER$GROUP1: USER$GROUP2: USER$GROUP2: USER$OTHER: This would require the following mapping rules (in the stated order). USER /~*/* /*/www/ PASS /~*/* /user$group1/*/www/* PASS /~*/* /user$group2/*/www/* PASS /~*/* /user$group3/*/www/* PASS /~*/* /user$other/*/www/* Of course vanilla mapping rules may be used to provide for special cases. For instance, if there is requirement for a particular, privileged account to have a user mapping that could be provided as in the following (rather exagerated) example. PASS /~system/* /sys$common/sysmgr/www/* USER /~*/* /*/www/ PASS /~*/* /user$disk/*/www/* Path SETings ------------ As of v12... see MAPSET.C [CONFIGFILE] ------------ The non-global [IncludeFile] equivalent [ConfigFile] designed for local, per-service configuration management can include a subset of the full set of mapping rules and settings. The following are ALLOWED: FAIL MAP PASS PROTECT REDIRECT SET [NO]ACCEPT= SET CHARSET= SET [NO]EXPIRED SET [NO]HTML= SET INDEX= SET [NO]MAP=ONCE SET QUERY-STRING= SET RESPONSE=HEADER= Those that cannot be used in [ConfigFile] are reported as errors during configuration load. MAP=ROOT Mapping ---------------- This path SETing allows for the more straight-forward mapping of virtual service paths into a virtual-service-specific directory structure. It ensures that for all applicable rules the specified string is prepended to the mapped path before it is converted into a file-system specification. This remove the need to do it with each virtual-service-specific rule. It needs to be redone or undone (e.g. set to empty) for each virtual service or when reverting to general rules. For example [[a.virtual.host]] set * map=root=/dka0/a/ # 'a' has no access to any scripts at all pass /* /* fail * [[b.virtual.host]] set * map=root=/dka0/b/ # 'b' are allowed to execute their own scripts exec /cgi-bin/* /cgi-bin/* pass /* /* fail * [[c.virtual.host]] # 'c' is allowed to execute the general server scripts exec /cgi-bin/* /cgi-bin/* set * map=root=/dka0/c/ pass /* /* fail * [[*]] set * map=root= exec /cgi-bin/* /cgi-bin/* pass /* /web/* fail * PERSONA-based User Scripting ---------------------------- For use with VMS V6.2 and greater (or 6.0 and 6.1 if compiled with the PERSONA_MACRO build option), with the /PERSONA qualifier active. The following rule set will allow user account based scripting. SET /~*/www/cgi-bin/* script=as=~ UXEC /~*/cgi-bin/* /*/www/cgi-bin/* USER /~*/* /*/www/* REDIRECT /~* /~*/ PASS /~*/* /dka0/users/*/* DECnet-based User Scripting --------------------------- If DECnet/OSU scripting is enabled then the following rule will activate a script in the specified user's directory (all other things being equal). Always map to some unique and specific subdirectory of the home directory. exec /~*/cgi-bin/* /0""::/where/ever/*/cgi-bin/* It's a little like the user-mapping rules: pass /~* /where/ever/* Mapping Conditionals -------------------- *************** * OBSOLESCENT * *************** ALTHOUGH MAPPING RULE CONDITIONALS DESCRIBED BELOW WILL CONTINUE TO BE SUPPORTED IN THE FORESEEABLE FUTURE THE PREFERED POST-7.2 MECHANISM FOR CONDITIONAL PROCESSING OF RULES IS PROVIDED BY THE META-CONFIG CONDITIONAL STRUCTURES AND DIRECTIVES. Conditionals are new to v4.4 and allow mapping rules to be applied conditionally! That is, the conditionals contain strings that are matched to specific elements of the request and only if matched are the corresponding rules applied. The conditionals may contain the '*' and '%' wildcards, and optionally be negated by an '!' at the start of the conditional string. Conditionals must follow the rule and are delimited by '[' and ']'. Multiple, space-separated conditions may be included within one '[...]'. This behaves as a logical OR (i.e. the condition is true if only one is matched). Multiple '[...]' conditionals may be included against a rule. These act as a logical AND (i.e. all must have at least one condition matched). The result of an entire conditional may be optionally negated by prefixing the '[' with a '!'. If a conditional is not met the line is completely ignored. Conditional rules are not used for 'backward' translation (i.e. from VMS to URL path) so another rule must exist somewhere to do these mappings. Conditional rules are only used by the HTTPd server, they are completely ignored by any scripts using the MapUrl.c module. [!]ac:accept 'Accept:' [!]al:accept-language 'Accept-Language:' [!]as:accept-charset 'Accept-Charset:' [!]ca: boolean, callout in progress? [!]ck:cookie 'Cookie:' [!]dr:string document root (set map=root=) [!]ex: boolean, extended file specification path [!]fo:host-name/address 'Forwarded:', proxies/gateways [!]ho:host-name/address client's of course! [!]hm:host network mask client mask, see immediately below [!]me:http-method GET, POST, ?, etc. [!]mp:string derived mapped path (after script or map rule) [!]no:string notepad keywords/tokens [!]pa:digit 1 if first pass, 2 if second pass through rules [!]pi:path-info request path information [!]qs:query-string request query string [!]rc:[digit] internally redirected count, 0..4, or boolean [!]rf:refering-URL 'Referer:' [!]rq:string request field (e.g. "Keep-Alive: 300") [!]ru:string request URI - non-decoded path string [!]sc:scheme request scheme 'http:', 'http', 'https:' or 'https' [!]sn:server-name server host name [!]sp:server-port server port [!]st:script-name if during second pass compare to script name [!]ua:user-agent 'User-Agent:' [!]vs:host-name/address virtual server, "Host:", destination [!]xf:client "X-Forwarded-For:" proxied cleint details The host-mask ('HM') directive is a dotted-decimal network address, a slash, then a dotted-decimal mask. For example "[HM: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. VERSION HISTORY --------------- 22-AUG-2022 MGD MapUrl_ReportNow() format massaging via MetaConCounter() 27-MAY-2021 MGD carve out path SETing functions to MAPSET.C module 14-APR-2021 MGD pass /whatever "200 $ CLI command" to RequestMappedToStatus() 20-MAR-2021 MGD SET response=var=asis 28-FEB-2021 MGD SET proxy=rework= 16-OCT-2020 MGD SET webdav=all and webdav=auth 26-SEP-2020 MGD bugfix; ODS=ADS missing from reporting 12-FEB-2020 MGD SET response=[no]csp= ("content-security-policy:") SET response=[no]cspro= ("..policy-report-only:") refactor MapUrl_ExplainPathSet() 20-JAN-2020 MGD quieten string overflow reports 30-JAN-2019 MGD SET response=200=203 for request tracking and log analysis 15-AUG-2018 MGD bugfix; for DECnet tasks when StringSliceValue() modified (25-MAY-2017 allow quote-delim inside space-delimited) 13-AUG-2018 MGD SET response=var=crlf SET response=var=lf SET response=var=none 06-APR-2018 MGD MapUrl__Map() allow one-to-one logical name content 05-APR-2018 MGD bugfix; longstanding MapUrl__Map() mutiple template wildcards when reverse mapping (search for the version log date) 25-JAN-2018 MGD SET dir=title=default SET dir=title=owner SET dir=title=remote SET dir=title= SET dir=title=this= 15-JUN-2017 MGD MapUrl_GuaranteeAccess() mapping as well as authorisation 25-MAY-2017 MGD bugfix; MapUrl_ExplainPathSet() response=header=add=.. 07-MAR-2017 MGD SET proxy=header=[=] 26-OCT-2015 MGD SET dict=[=] SET http2=streams=max= SET http2=protocol=1.1 SET http2=write=[low|normal|high] SET http2=send=goaway[=] SET http2=send=ping SET http2=send=reset[=] SET sslcgi=apache_mod_ssl_client SET sslcgi=apache_mod_ssl_extens 17-APR-2015 MGD #define MAP_BUFFER_SIZE for fundamental mapping buffers 04-APR-2015 MGD bugfix; MapUrl_ExplainPathSet() ->ResponseChunked 24-MAR-2015 MGD SET response=sts= (Strict-Transport-Security: header) 21-NOV-2014 MGD MapUrl_SetClientAddress() and MapUrl_ResetClientAddress() call WatchFilterClientService() if client address changed 06-NOV-2014 MGD SET client=[forwarded|if=forwarded|literal=|reset| if=xforwardedfor|xforwardedfor] 06-SEP-2014 MGD SET dir=font=[inherit|monospace(D)] SET dir=style=TABLE (new default) SET ods=name=8bit, ods=name=utf8, ods=name=default SET webdav=[no]hidden SET webdav=meta=dir= 28-SEP-2013 MGD SET cors= 27-SEP-2013 MGD SET dir=versions=|* 17-AUG-2013 MGD put=rfm=[STM|STMCD|UDF] added to FIX512,STMLF bugfix; MapUrl__Map() MAPURL_REPORT_MATCH_RULENOCOND 25-JUN-2013 MGD SET dir=delimit= SET dir=[no]ilink SET dir=style=sort (plus the dir=style=2) SET dir=sort=[+|-] SET dir=target= SET dir=these=[,] 30-MAY-2013 MGD bugfix; MapUrl__Map() reverse mapping wildcard copy 15-SEP-2011 MGD SET map=uri bugfix; map=noonce 06-MAR-2011 MGD SET cache=[no]cookie 26-FEB-2011 MGD bugfix; set rule 'CacheSetting' boolean with any CACHE=.. 02-JAN-2011 MGD SET regex= 07-SEP-2010 MGD SET proxy=tunnel=request= 18-JUL-2010 MGD Uni Malaga pushes report items from !3ZL to !4ZL :-) 01-JUL-2010 MGD bugfix; MapUrl_ControlReload() 19-JUN-2010 MGD put=max=* represents no (effective) limit 27-FEB-2010 MGD SET proxy=chain=cred= 09-FEB-2010 MGD SET script=lifetime= SET response=HTTP=original 23-JAN-2010 MGD SET service= SET websocket= SET notimeout (short-hand for timeout=none,none,none) 11-JAN-2010 JPP MapUrl__Map() increase some buffer sizes 05-SEP-2009 MGD allow "pass:-1" to indicate reverse-mapping 23-AUG-2009 MGD SET css= bugfix; MapUrl__Map() to URL use request ODS not path ODS 21-JUN-2009 MGD SET put=max=, put=rfm=[FIX512|STMLF] 08-OCT-2008 MGD bugfix; MapUrl__Map() proxy 'fall-thru' 19-APR-2008 MGD SET script=agent=as= 23-MAY-2007 MGD refine loading and mapping of path SETings, MapUrl_Map()/__Map() now have a REQUEST_PATHSET parameter (to better decouple file-system mapping and path SETing) 11-MAY-2007 MGD bugfix; MapUrl_Map() auth agent modifying path SETings 01-MAY-2007 MGD SET access=[no]access, access=[no]profile, access=[no]read, access=[no]write, webdav=[no]access, webdav=[no]profile, webdav=[no]read, webdav=[no]write, webdav=[no]lock, webdav=[no]prop, webdav=lock=timeout= 22-APR-2006 MGD SET proxy=[no]affinity 26-JAN-2006 MGD increase MapUrl__Map() WildBuffer[] storage to 4096 26-OCT-2005 MGD bugfix; MapUrl__Map() SCRIPT result copy not checking for null resulting in occasional overflow error status 10-SEP-2005 JPP SET proxy=reverse=[no]auth 01-JUN-2005 MGD SET script=symbol=[no]truncate 21-MAY-2005 MGD SET throttle=/ per-user throttle 08-JAN-2005 MGD SET script=control=<...> 16-DEC-2004 MGD SET response=[no]chunked 04-NOV-2004 MGD SET script=syntax=[no]unix 16-OCT-2004 MGD SET response=gzip=<...>, SET script=body=[no]decode, allow a throttle of zero to be set, throttle=none to reset previously mapped throttle, unbundle some functionality into MAPCON.C, MAPODS.C and MAPUSER.C modules (been wanting to do this for ages) 24-SEP-2004 MGD bugfix; auth=revalidate= is minutes not seconds 31-AUG-2004 MGD SET report=tunnel 12-AUG-2004 MGD bugfix; parse timeout=n,n,n allowing to specify "none" 20-JUL-2004 MGD SET timeout=keepalive= superceded by timeout=persistent= 20-APR-2004 MGD actual on-disk structure for each PASS result (ODS-2 or ODS-5) is applied to a path unless otherwise SET with ODS=, don't bother translating a PASS unless it looks file-system (consumes a few less cycles for things like proxy) 08-MAR-2004 MGD MapUrl_Map() increase wildcard buffer space 26-FEB-2004 MGD SET script=default= 13-JAN-2004 MGD review string SETings for empty string behaviour 10-JAN-2004 MGD SET ssi=exec= 08-JAN-2004 MGD SET response=header=[no]add[=""] 16-DEC-2003 MGD mapping now URL-encodes a redirect wildcard path portions 20-NOV-2003 MGD SET proxy=reverse=location, proxy=reverse=verify 28-SEP-2003 MGD when do a callout path mapping use a 'throw-away' path SETing structure to prevent modification of request itself, SET path=set=[no]ignore, path=set=[no]request prevent ODS-2 directory paths from containing single periods 01-SEP-2003 MGD SET map=root= (groan %^p) 'DR' conditional (parallels metacon "document-root:") 07-JUL-2003 MGD SET script=command=, SET cache=[no]cgi, cache=expires=.., cache=[no]file, cache=[no]net, cache=maxkbytes=.., cache=[no]nph, cache=[no]script, cache=[no]ssi, response=header=[append|full|none] bugfix; do not allow SET mapping during a callout 27-JUN-2003 MGD bugfix; request Html.. memory allocation (jpp@esme.fr) 24-MAY-2003 MGD SET cache=[no]permanent, cache=max= 09-MAY-2003 MGD regular expression support, SET notepad=[+] (associated 'NO' conditional), SET map=restart, SET proxy=unknown 'RQ' conditional (parallels metacon "request:") 22-APR-2003 MGD improve SET rule error reporting (if no rule or no template) 02-APR-2003 MGD SET proxy=xforwardedfor=[disabled|enabled|address|unknown] SET proxy=forwarded=[disabled|by|for|address] added 'XF' conditional, removed 'UD' (meaningless) 26-MAR-2003 MGD SET alert=nnn (were 'nnn' can be 500, 599, 403, etc.) 08-MAR-2003 MGD SET html=[bodytag|header|headertag|footer|footertag]=[..], dir=style[=default|original|anchor|htdir] 15-FEB-2002 MGD SET [no]search=none, script=params=+(name=value) concatenates to any existing 17-JAN-2003 MGD general (non-RTE) run-time allowed with (!..) syntax, both run-time specifications allowed with SCRIPT rule SET cgiplusin=[none|cr|lf|crlf], SET cgiplusin=eof, SET script=query=none, SET script=path=find 10-JAN-2003 MGD SET script=query=relaxed 16-NOV-2002 MGD SET auth=all (all access must be authorized or fail) 06-NOV-2002 MGD added 'MP', 'RC' and 'ST' conditionals 12-OCT-2002 MGD refine metacon reporting, refine mapping rule processing to ensure that paths with forbidden syntax (e.g. multiple '/') generate RMS bad syntax 05-OCT-2002 MGD 'positional' wildcards, SET query-string=, provide 'rqptr->MetaConPass', confine script=as= to first pass (script resolution) added 'PA' conditional 24-SEP-2002 MGD added 'PI' and 'RU' conditionals 16-SEP-2002 MGD MapOdsElementsToVms() excise parent directory syntax, SET path [no]map=ellipsis (off by default), SET report=4nn=nnn for mapping HTTP status 14-SEP-2002 MGD only use MapUrl_VmsUserName() path ODS if not already set, SET dir=charset= directory listing charset mapping rule 10-SEP-2002 MGD add en/decoding for Advanced Server (Samba) file names 15-AUG-2002 MGD SET alert=map, alert=auth, alert=end variants for ALERT, SET ods=2, ods=5, now ods=pwk and ods=sri for below, rework ..UrlToVms() and ..VmsToUrl() to allow for mapping to and from SRI encoding (NFS) and PATHWORKS encoding (and VMS to URL functions no longer explicitly URL-encode) 07-AUG-2002 MGD bugfix; 'script' and 'exec' MetaConParseReset() state 04-AUG-2002 MGD allow for 'pass /* 400' (i.e. no trailing message), bugfix; template/result wildcard checking for scripting rules 31-MAY-2001 MGD SET dir=access, dir=noaccess, dir=access=selective, dir=impliedwildcard, dir=noimpliedwildcard, dir=wildcard, dir=nowildcard 13-MAY-2002 MGD SET auth=SYSUAF=pwdexpURL= 07-APR-2002 MGD exchange DEVICE:[DIRECTORY]FILE.TYPE for the hopefully more informative NO:[REVERSE.MAPPING.FOR.THIS]FILE.PATH 14-MAR-2002 MGD bugfix; throttle report (jf.pieronne@laposte.net) 14-JAN-2002 MGD added '**' and '*!' wildcard match and discard to template, eliminate device:[.directory] (multiple consecutive '/'), bugfix; arrghhh - wildcard substitution in MapUrl__Map() 18-NOV-2001 MGD path SETings may now trail any rule with a result string, and are applied to the matched path before mapping occurs, SET http=accept-charset=, accept=lang 28-OCT-2001 MGD bugfix; DECnet user script mapping 13-OCT-2001 MGD ensure a conditional is not mistaken for a missing template, script=params=(name=value[,name="value1 value2"]), proxy=bind=IP-address, proxy=chain=host:port, alert, auth=revalidate=hh:mm:ss, bugfix; wildcard substitution in MapUrl__Map() 25-AUG-2001 MGD meta-config (even workin' on my birthday, sigh) 04-AUG-2001 MGD support module WATCHing 15-MAY-2001 MGD bugfix; remove 'RU' conditional (mapping before auth!) 10-MAY-2001 MGD modify throttle parameter meanings, set rule SCRIPT=CPU= for limiting script process CPU 13-APR-2001 MGD add queue length to THROTTLE=n[,n,n,hh:mm:ss,hh:mm:ss] add SCRIPT=BIT-BUCKET=hh:mm:ss, bugfix; conditions for NetThisVirtualService() call 05-APR-2001 MGD bugfix; 'HM' network mask processing 28-MAR-2001 MGD EXEC of file type 13-MAR-2001 MGD set rules THROTTLE=n[,n,n,n] & TIMEOUT=n,n,n "hh:mm:ss" for providing a more versatile period 28-FEB-2001 MGD OdsLoadTextFile(), OdsParseTextFile(), [IncludeFile] 22-DEC-2000 MGD use of backslash to escape non-wildcard reserved characters in rule and conditional strings (i.e. \] for ]) 19-NOV-2000 MGD bugfix; mapping '/' 10-NOV-2000 MGD bugfix; MapOdsUrlToVms() final ';' 01-OCT-2000 MGD set rule SCRIPT=AS=, rework mapping code somewhat, MapUrl_VmsUserNameCache() on reset free entries 31-AUG-2000 MGD bugfix; MapOdsUrlToVms() escaped version semi-colon 24-JUN-2000 MGD add network-mask to conditionals, persistent run-time environment in EXEC rule (...), change 'HH' to 'VS' (virtual service) backward compatible 06-APR-2000 MGD set rules SSLCGI= and MAP= 04-MAR-2000 MGD use FaolToNet(), et.al. 02-JAN-2000 MGD config file opened via ODS module, detection of a path's on-disk-structure (ODS-2 or ODS-5), modify URL->VMS and VMS->URL mapping for extended syntax, avoid confusion; redirect indicators changed from '^' and '@' for local and remote, to '<' and '>' respectively, bugfix; trailing wildcard in set rule comparison 24-OCT-1999 MGD groan; ensure storage does not overflow!! (plus copious quantities of self-flagellation), SYSUAF-based user directory mapping via a "USER" rule, CGIprefix, [no]profile and [no]authonce SET rules, happy seventh wedding anniversary 12-SEP-1999 MGD virtual services modifications 10-MAY-1999 MGD conditional mapping on HTTP cookie and referer, increase the size of scratch storage, mapping endless-loop detection 16-JAN-1999 MGD allow for proxy service mappings by removing requirement for template and result paths to be absolute ("/...") paths (every time I have to deal with this module I groan) 10-JAN-1999 MGD bugfix; pass rules not returning mapped path 17-OCT-1998 MGD SET mapping rule, virtual services via "[[virtual-host:virtual-port]]", "qs:" query string conditional, invalid RMS file name/character substitution configurable 12-AUG-1998 MGD MapOdsVmsToUrl() now only optionally absorbs MFD 19-JUL-1998 MGD 'E'xec DECnet-based user scripts 10-MAR-1998 MGD allow for local redirects (using '^' detect character) 07-FEB-1998 MGD added "sc:" scheme/protocol conditional for SSL support 20-DEC-1997 MGD added DECnet support in _UrlToVms(), result paths can now begin with an '*' 25-OCT-1997 MGD added "as:", "fo:", and "hh:" conditionals, bugfix; MsgFor() required request pointer to find langauge 15-SEP-1997 MGD MsgFor() was not returning a leading-null message (does now), HTTP status code rules (e.g. "pass /no/* 403 forbidden"), removed two consecutive slashes inhibiting rule mapping 07-SEP-1997 MGD added "sn:" and "sp:" conditionals 09-AUG-1997 MGD v4.3, message database, mapping "conditionals", more cart-wheels, hand-stands and contortions (see 01-OCT-96) 05-JUN-1997 MGD v4.2, CGIplus "exec+" and "script+" rules 01-FEB-1997 MGD HTTPd version 4, major changes for form-based config 01-OCT-1996 MGD not proud of how I shoe-horned the reporting features in, good example of slop-down or object-disoriented programming, :^( will rework the whole thing one day, sigh! bugfix; requiring use of 'TheFirstRuleWillBeTheNextRule' 06-APR-1996 MGD modified conversion of non-VMS characters in MapOdsUrlToVms() 15-FEB-1995 MGD bugfix; redirect rule 01-DEC-1995 MGD v3.0 24-MAR-1995 MGD bugfix; end-of-string sometimes not detected when matching 20-DEC-1994 MGD revised for multi-threaded HTTP daemon 20-JUN-1994 MGD single-threaded daemon */ #endif /* COMMENTS_WITH_COMMENTS */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include "wasd.h" #define WASD_MODULE "MAPURL" /* fundamental buffer size */ #ifdef __ALPHA #define MAP_BUFFER_SIZE (8192 * 2) #else /* IA64 and whatever else may follow */ #define MAP_BUFFER_SIZE (8192 * 4) #endif /**********/ /* macros */ /**********/ #define MAPURL_RMS_SUBSTITUTION_DEFAULT '$' #define WATCH_MODULE_DETAIL (WATCH_MODULE(WATCH_MOD_MAPURL) && \ WATCH_MODULE(WATCH_MOD__DETAIL)) /******************/ /* global storage */ /******************/ BOOL MapUrlExtensionMethod, MapUrlPathOds5; MAPPING_META MappingMeta; MAPPING_META *MappingMetaPtr; /********************/ /* external storage */ /********************/ extern BOOL AuthPromiscuous, AuthProtectRule, AuthPolicySysUafRelaxed, CliOdsExtendedDisabled, CliOdsExtendedEnabled, OdsExtended; extern int EfnWait, ServerPort, ServerHostNameLength; extern unsigned long SysPrvMask[]; extern char *FaoUrlEncodeTable[]; extern char ErrorSanityCheck[], ErrorWatchSysFao[], ServerHostName[], ServerHostPort[], SoftwareID[]; extern LIST_HEAD MapUrlUserNameCacheList; extern int MapUrlUserNameCacheEntries; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern META_CONFIG *MetaGlobalMappingPtr; extern MSG_STRUCT Msgs; extern SYS_INFO SysInfo; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* This overly-long function (sorry) is not AST reentrant, but when executed within the context of the HTTPd, is executed either without the possibility of AST interruption, or during AST processing and so provides atomic functionality. Pointers to any strings returned must be used immediately (before leaving AST context), or copied into storage maintained by the calling routine. Maps from URL format to VMS file specification, and REVERSE-MAPS from VMS file specification to URL format. Maps scripts. Always returns a pointer to char. An error message is detected by being returned as a pointer to a string beginning with a null-character! The message begins from character one. Call with 'PathPtr' pointing at a string containing the URL path and 'VmsPtr' pointing to the storage to contain the mapped VMS equivalent. Call with 'VmsPtr' pointing to a VMS file specification (dev:[dir]file.ext) and 'PathPtr' pointing to an empty string, used as storage to contain the REVERSE-MAPPED file specification. Call with 'PathPtr' pointing at a string containing the URL path, 'VmsPtr' pointing to storage to contain the mapped VMS equivalent, 'ScriptPtr' pointing to storage for the URL format script path and 'ScriptVmsPtr' pointing to storage to contain the VMS specification of the script procedure/image. The MapUrl_Map() just wraps MapUrl__Map() so that it's function can be WATCHed. See alter-egos defined in MapUrl.h */ char* MapUrl_Map ( char *PathPtr, int SizeOfPathPtr, char *VmsPtr, int SizeOfVmsPtr, char *ScriptPtr, int SizeOfScriptPtr, char *ScriptVmsPtr, int SizeOfScriptVmsPtr, char *RunTimePtr, int SizeOfRunTimePtr, int *PathOdsPtr, REQUEST_STRUCT *rqptr, REQUEST_PATHSET *PathSetPtr ) { BOOL VmsToPath; char *cptr, *sptr; char OdsNote [32]; /*********/ /* begin */ /*********/ if (!rqptr || !WATCHPNT(rqptr)) { cptr = MapUrl__Map (PathPtr, SizeOfPathPtr, VmsPtr, SizeOfVmsPtr, ScriptPtr, SizeOfScriptPtr, ScriptVmsPtr, SizeOfScriptVmsPtr, RunTimePtr, SizeOfRunTimePtr, PathOdsPtr, rqptr, PathSetPtr); if (rqptr) { rqptr->MetaConPass = 0; rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL; } return (cptr); } if (!WATCH_CATEGORY(WATCH_MAPPING)) { cptr = MapUrl__Map (PathPtr, SizeOfPathPtr, VmsPtr, SizeOfVmsPtr, ScriptPtr, SizeOfScriptPtr, ScriptVmsPtr, SizeOfScriptVmsPtr, RunTimePtr, SizeOfRunTimePtr, PathOdsPtr, rqptr, PathSetPtr); if (rqptr) { rqptr->MetaConPass = 0; rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL; } return (cptr); } #if WATCH_CAT VmsToPath = false; if (PathPtr && PathPtr[0]) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "PATH !AZ", PathPtr); else if (VmsPtr && VmsPtr[0]) { VmsToPath = true; WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "VMS !AZ", VmsPtr); } cptr = MapUrl__Map (PathPtr, SizeOfPathPtr, VmsPtr, SizeOfVmsPtr, ScriptPtr, SizeOfScriptPtr, ScriptVmsPtr, SizeOfScriptVmsPtr, RunTimePtr, SizeOfRunTimePtr, PathOdsPtr, rqptr, PathSetPtr); if (VmsToPath) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "RESULT !AZ", cptr); else { if (!PathPtr) PathPtr = ""; if (!VmsPtr) VmsPtr = ""; if (!ScriptPtr) ScriptPtr = ""; if (!ScriptVmsPtr) ScriptVmsPtr = ""; if (!RunTimePtr) RunTimePtr = ""; WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "RESULT"); if (!cptr[0] && cptr[1]) WatchDataFormatted ("Error: !AZ\n", cptr+1); else if (cptr[0] == '\1') WatchDataFormatted ("Redirect: !AZ\n", cptr+1); else { switch (PathOdsPtr ? *PathOdsPtr : rqptr->PathOds) { case MAPURL_PATH_ODS_2 : sptr = " (ODS-2)"; break; case MAPURL_PATH_ODS_5 : sptr = " (ODS-5)"; break; case MAPURL_PATH_ODS_ADS : sptr = " (ADS)"; break; case MAPURL_PATH_ODS_PWK : sptr = " (PWK)"; break; case MAPURL_PATH_ODS_SMB : sptr = " (SMB)"; break; case MAPURL_PATH_ODS_SRI : sptr = " (SRI)"; break; default : sptr = " (ods-2)"; } WatchDataFormatted ("\ Mapped: !AZ\n\ Translated: !AZ!AZ\n\ Script: !AZ\n\ Script-File: !AZ\n\ Run-Time: !AZ\n", cptr, VmsPtr, VmsPtr[0] ? sptr : "", ScriptPtr, ScriptVmsPtr, RunTimePtr); } } if (rqptr) { rqptr->MetaConPass = 0; rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL; } return (cptr); #endif /* WATCH_CAT */ } char* MapUrl__Map ( char *PathPtr, int SizeOfPathPtr, char *VmsPtr, int SizeOfVmsPtr, char *ScriptPtr, int SizeOfScriptPtr, char *ScriptVmsPtr, int SizeOfScriptVmsPtr, char *RunTimePtr, int SizeOfRunTimePtr, int *PathOdsPtr, REQUEST_STRUCT *rqptr, REQUEST_PATHSET *PathSetPtr ) { static char DerivedPathBuffer [MAP_BUFFER_SIZE+128], PathBuffer [MAP_BUFFER_SIZE+128], VmsBuffer [MAP_BUFFER_SIZE+128]; static char CommentFao [] = "!AZ!AZ\n"; static char NutherFileFao [] = "!AZ!AZ\n"; BOOL EvanescentZeroed, ExecFileType, LoadingRules, MapEllipsis, MapPathToVms, StatusCodeMapping, SubstituteForUserName; int idx, widx, status, InConfigLevel, MapRootLength, MetaConPass, MetaConRestartCount, PathOds, SetPathIgnore, TotalLength, WatchThisMatch, WatchThisOne, WildStringCount; unsigned short Length; char ch, RmsSubChar; char *cptr, *eptr, *pptr, *rptr, *sptr, *tptr, *uptr, *zptr, *MapRootPtr; char *WildString [REGEX_PMATCH_MAX]; char Scratch [MAP_BUFFER_SIZE+128], UserDefault [256], VmsUserMappedBuffer [256], WildBuffer [(MAP_BUFFER_SIZE*4)+128]; MAP_RULE_META *mrptr; MAP_SET_META *mrpsptr; METACON_LINE *mclptr; REQUEST_PATHSET EvanescentPathSet; REQUEST_PATHSET *rqpsptr; regex_t *pregptr; regmatch_t pmatch [REGEX_PMATCH_MAX]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_Map() !UL !&Z !UL !&Z !UL !&Z !UL", SizeOfPathPtr, PathPtr, SizeOfVmsPtr, VmsPtr, SizeOfScriptPtr, ScriptPtr, SizeOfScriptVmsPtr); /* if just loading the rules then free any previous set */ if (LoadingRules = (!PathPtr && !VmsPtr && !ScriptPtr)) MetaConUnload (&MetaGlobalMappingPtr, &MapUrl_ConfigUnloadLineData); if (!MetaGlobalMappingPtr) { /*********************/ /* read mapping file */ /*********************/ MapUrl_ConfigLoad (&MetaGlobalMappingPtr); } /* if just (re)loading rules then return now */ if (LoadingRules) return ("\0\0"); /*****************************************/ /* map from the URL or VMS to VMS or URL */ /*****************************************/ if (!PathPtr) { SET2(PathBuffer,'\0\0'); PathPtr = PathBuffer + 1; SizeOfPathPtr = sizeof(PathBuffer); } if (!VmsPtr) { *(VmsPtr = VmsBuffer) = '\0'; SizeOfVmsPtr = sizeof(VmsBuffer); } /* making 'ScriptPtr' non-empty means "exec" and "script" are ignored */ if (!ScriptPtr) ScriptPtr = "?"; if (PathPtr[0]) { /* if the URL is not an empty string then force the conversion to VMS */ MapPathToVms = true; *VmsPtr = '\0'; MetaConPass = 1; } else { /* URL was an empty string, convert from VMS to URL */ MapPathToVms = false; /* generate a URL-style version of the VMS specification */ MapOdsVmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, true, rqptr->PathOds); MetaConPass = -1; } /***********************/ /* loop thru the rules */ /***********************/ if (!MetaGlobalMappingPtr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_NO_RULES)-1); WatchThisOne = WatchThisMatch = 0; if (WATCHPNT(rqptr)) { if (WATCH_CATEGORY(WATCH_MAPPING)) WatchThisOne = WATCH_MAPPING; if (WATCH_CATEGORY(WATCH_MATCH)) WatchThisMatch = WATCH_MATCH; } InConfigLevel = MapRootLength = PathOds = 0; RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT; EvanescentZeroed = MapEllipsis = SetPathIgnore = false; MapRootPtr = NULL; MetaConRestartCount = 0; if (rqptr) { rqptr->MetaConPass = MetaConPass; rqptr->MetaConRestartCount = MetaConRestartCount; rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL; } if (!PathSetPtr) { PathSetPtr = &EvanescentPathSet; memset (&EvanescentPathSet, 0, sizeof(EvanescentPathSet)); EvanescentZeroed = true; } rqpsptr = PathSetPtr; mclptr = NULL; MetaConParseReset (MetaGlobalMappingPtr, true); for (;;) { mclptr = MetaGlobalMappingPtr->ParsePtr = MetaGlobalMappingPtr->ParseNextPtr; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z\n", mclptr, mclptr->Size, mclptr->Token, mclptr->Number, mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr); /* if terminating empty "line" */ if (!mclptr->Size) break; mclptr->WatchThisOne = WatchThisOne; if (mclptr->Token == METACON_TOKEN_COMMENT) { if (WatchThisOne) { if (SAME4(mclptr->TextPtr, '!#+#')) { /* from InConfigLoad() add a level */ WatchDataFormatted ("!AZ!AZ\n", MetaConCounter (mclptr, InConfigLevel), mclptr->TextPtr + 5); InConfigLevel++; } else if (SAME4(mclptr->TextPtr, '!#-#')) { /* from InConfigLoad() remove a level */ if (InConfigLevel) InConfigLevel--; } else WatchDataFormatted ("!AZ!AZ\n", MetaConCounter (mclptr, InConfigLevel), mclptr->TextPtr); } MetaGlobalMappingPtr->ParseNextPtr = (METACON_LINE*)((char*)mclptr + mclptr->Size); continue; } if (mclptr->Token == METACON_TOKEN_TEXT) { /* get required data, adjust the parse context to the next "line" */ cptr = mclptr->TextPtr; mrptr = mclptr->LineDataPtr; MetaGlobalMappingPtr->ParseNextPtr = (METACON_LINE*)((char*)mclptr + mclptr->Size); } else if (mclptr->Token != METACON_TOKEN_DIRECTORY) { /* not a simple text line, have meta-config parse this one for us */ cptr = MetaConParse (rqptr, MetaGlobalMappingPtr, &mclptr, WatchThisOne); /* if the end of rules */ if (!cptr) break; /* if error string */ if (!*cptr && *(cptr+1)) return (cptr); /* if inline rule and expression was false */ if (SAME4 (cptr, '\0\0\0\1')) continue; mrptr = mclptr->LineDataPtr; } if (mclptr->Token == METACON_TOKEN_DIRECTORY) { /* get required data, adjust the parse context to the next "line" */ cptr = mclptr->TextPtr + sizeof("[ConfigDirectory]"); while (*cptr && ISLWS(*cptr)) cptr++; zptr = (sptr = rqptr->ConfigDirectory) + sizeof(rqptr->ConfigDirectory)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; rqptr->ConfigDirectoryLength = sptr - rqptr->ConfigDirectory; MetaGlobalMappingPtr->ParseNextPtr = (METACON_LINE*)((char*)mclptr + mclptr->Size); continue; } /* there was a problem in initializing the rule */ if (!mrptr) { if (WatchThisOne) WatchDataFormatted ("?!AZ\n", cptr); continue; } if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!&X !UL !&Z !&Z !&Z\n", mrptr, mrptr->RuleType, mrptr->TemplatePtr, mrptr->ResultPtr, mrptr->ConditionalPtr); /* hmmm, endless loop!! */ if (MetaConPass > 2) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); if (rqptr->rqPathSet.MapUri) { /* substitute the request URI (raw path plus query) for rewriting */ rqptr->rqPathSet.MapUri = false; zptr = (pptr = PathPtr = PathBuffer+1) + sizeof(PathBuffer)-1; for (cptr = rqptr->rqHeader.RequestUriPtr; *cptr && pptr < zptr; *pptr++ = *cptr++); if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; } /* if 'ScriptPtr' is not to be used then ignore "exec" and "script" */ if (ScriptPtr[0] && (mrptr->RuleType == MAPURL_RULE_EXEC || mrptr->RuleType == MAPURL_RULE_SCRIPT || mrptr->RuleType == MAPURL_RULE_UXEC)) { if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_NOT, SetPathIgnore); continue; } if (cptr = MapUrl_GuaranteeAccess (rqptr, PathPtr)) return (cptr); /*****************************************************************/ /* compare the URL with the template/result in the mapping entry */ /*****************************************************************/ if (mrptr->RuleType == MAPURL_RULE_SET || mrptr->RuleType == MAPURL_RULE_PROTECT) { /*********************/ /* SET/PROTECT rules */ /*********************/ /* cheap check obvious non-/match before using expensive functions */ tptr = mrptr->TemplatePtr; pregptr = &mrptr->RegexPregTemplate; pptr = PathPtr; if (Config.cfMisc.RegexSyntax && *tptr == REGEX_CHAR) tptr++; /* while matching vanilla characters */ while ((isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_') && to_lower(*pptr) == to_lower(*tptr)) { pptr++; tptr++; } /* if non-matching vanilla character */ if (isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_') { if (WatchThisMatch) WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO !&Z", tptr); if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_NOT, SetPathIgnore); continue; } /* if a trailing wildcard then it's a match already! */ if (SAME2(tptr,'*\0')) { if (WatchThisMatch) WatchThis (WATCHITM(rqptr), WATCH_MATCH, "YES"); } else { /* expensive comparison of path and template */ if (!StringMatchAndRegex (rqptr, PathPtr, mrptr->TemplatePtr, SMATCH_STRING_REGEX, pregptr, NULL)) { /* not matched then straight to next rule */ if (!WatchThisOne) continue; MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_NOT, SetPathIgnore); continue; } } if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MATCHED"); } else { /***************/ /* other rules */ /***************/ /* when mapping VMS to URL then ignore all BUT "pass" & "set" rules */ if (!MapPathToVms && mrptr->RuleType != MAPURL_RULE_PASS) continue; /* if reverse-mapping VMS to URL use the result if available */ if (!MapPathToVms && mrptr->ResultPtr[0]) { if (MapRootPtr) { /* if a mapping root is set then eliminate this first */ cptr = MapRootPtr; pptr = PathPtr; while (*cptr && *pptr && to_lower(*cptr) == to_lower(*pptr)) { cptr++; pptr++; } if (*cptr || !*pptr) { /* the path and root do not match */ if (!WatchThisMatch) continue; WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO (root) !&Z", cptr); continue; } } cptr = tptr = mrptr->ResultPtr; pregptr = NULL; } else { cptr = tptr = mrptr->TemplatePtr; pregptr = &mrptr->RegexPregTemplate; } /* cheap check obvious non-/match before using expensive functions */ if (Config.cfMisc.RegexSyntax && *tptr == REGEX_CHAR) tptr++; pptr = PathPtr; /* while matching vanilla characters */ while ((isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_') && to_lower(*pptr) == to_lower(*tptr)) { pptr++; tptr++; } /* if non-matching vanilla character */ if (isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_') { if (WatchThisMatch) WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO !&Z", tptr); if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_NOT, SetPathIgnore); continue; } /* if a trailing wildcard then it's a match already! */ if (SAME2(tptr,'*\0')) { if (WatchThisMatch) WatchThis (WATCHITM(rqptr), WATCH_MATCH, "YES"); /* simulate the setting of the 'pmatch' data */ pmatch[0].rm_so = 0; pmatch[1].rm_so = pptr - PathPtr; while (*pptr) pptr++; pmatch[0].rm_eo = pmatch[1].rm_eo = pptr - PathPtr; for (idx = 2; idx < REGEX_PMATCH_MAX; idx++) pmatch[idx].rm_so = pmatch[idx].rm_eo = -1; } else { /* expensive comparison of path and template */ if (!StringMatchAndRegex (rqptr, PathPtr, cptr, SMATCH_STRING_REGEX, pregptr, &pmatch)) { /* not matched then straight to next rule */ if (!WatchThisOne) continue; MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_NOT, SetPathIgnore); continue; } } /************/ /* matched! */ /************/ /* copy using 'regmatch_t' data, strings into 'WildBuffer' */ zptr = (sptr = WildBuffer) + sizeof(WildBuffer); for (idx = WildStringCount = 0; idx < REGEX_PMATCH_MAX; idx++) { WildString[idx] = ""; if (pmatch[idx].rm_so == -1 || pmatch[idx].rm_eo == -1) continue; WildString[WildStringCount++] = sptr; pptr = PathPtr + pmatch[idx].rm_so; cptr = PathPtr + pmatch[idx].rm_eo; while (pptr < cptr && sptr < zptr) *sptr++ = *pptr++; if (sptr >= zptr) break; *sptr++ = '\0'; } if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); /* the actual number of wildcard/group matched strings */ if (WildStringCount) WildStringCount--; if (WATCH_MODULE(WATCH_MOD_MAPURL)) { WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MATCHED"); StringWatchPmatch (PathPtr, &pmatch); WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "WILDSTRING"); for (idx = 1; idx <= WildStringCount; idx++) WatchDataFormatted ("!UL. !&Z\n", idx, WildString[idx]); } } if (mrptr->ConditionalPtr[0]) { /***************/ /* conditional */ /***************/ /* when not mapping using a request then ignore conditionals */ if (rqptr) { /* if condition not met then continue */ if (!MapConString ((REQUEST_STRUCT*)rqptr, mrptr, rqpsptr)) { if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_RULE, SetPathIgnore); continue; } /* report rule and conditional matched */ if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_RULECOND, SetPathIgnore); } if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "CONDITIONAL MATCHED"); } else { /* report rule, there was no conditional */ if (WatchThisOne) MapUrl_WatchRule (rqptr, mrptr, mclptr, InConfigLevel, PathPtr, PathOds, MAPURL_REPORT_MATCH_RULENOCOND, SetPathIgnore); } if (mrptr->PathSet) { /****************/ /* path SETings */ /****************/ mrpsptr = &mrptr->mpPathSet; if (mrpsptr->MapSetIgnore) { /* takes effect immediately */ SetPathIgnore = true; continue; } else if (mrpsptr->MapSetNoIgnore) SetPathIgnore = false; /* ignoring path SETings (except for the above of course ;^) */ if (SetPathIgnore) continue; /***********************/ /* being ignored? - no */ /***********************/ if (mrpsptr->MapRestart) { MetaConRestartCount++; if (WatchThisOne) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "RESTART !UL", MetaConRestartCount); if (MetaConRestartCount > MAPURL_RESTART_MAX) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); if (rqptr) rqptr->MetaConRestartCount = MetaConRestartCount; MetaConParseReset (MetaGlobalMappingPtr, true); continue; } /* some SETings must be applied in this function! */ if (!(rqptr->AgentRequestPtr || rqptr->rqCgi.CalloutInProgress)) { if (mrpsptr->MapSetRequest) rqpsptr = &PathSetPtr; else if (mrpsptr->MapSetNoRequest) { rqpsptr = &EvanescentPathSet; if (!EvanescentZeroed) { memset (&EvanescentPathSet, 0, sizeof(EvanescentPathSet)); EvanescentZeroed = true; } } } if (mrpsptr->NoMapEllipsis) rqpsptr->MapEllipsis = MapEllipsis = false; else if (mrpsptr->MapEllipsis) rqpsptr->MapEllipsis = MapEllipsis = true; if (mrpsptr->MapRootPtr) { /* empty string resets */ if (mrpsptr->MapRootLength) { MapRootPtr = mrpsptr->MapRootPtr; MapRootLength = mrpsptr->MapRootLength; rqpsptr->MapRootPtr = VmGetHeap (rqptr, mrpsptr->MapRootLength+1); memcpy (rqpsptr->MapRootPtr, mrpsptr->MapRootPtr, mrpsptr->MapRootLength+1); } else { MapRootPtr = rqpsptr->MapRootPtr = NULL; MapRootLength = 0; } } if (mrpsptr->RmsSubChar) RmsSubChar = mrpsptr->RmsSubChar; if (rqptr->AgentRequestPtr) { /* bypass evanecsent structure by SETing directly into request! */ if (MetaConPass == 1 && mrpsptr->ScriptAgentAsPtr) { /* empty string resets */ if (mrpsptr->ScriptAgentAsLength) { rqptr->rqPathSet.ScriptAgentAsPtr = VmGetHeap (rqptr, mrpsptr->ScriptAgentAsLength+1); memcpy (rqptr->rqPathSet.ScriptAgentAsPtr, mrpsptr->ScriptAgentAsPtr, mrpsptr->ScriptAgentAsLength+1); } else rqptr->rqPathSet.ScriptAgentAsPtr = NULL; } } if (MetaConPass == 1 && mrpsptr->ScriptAsPtr) { /* empty string resets */ if (mrpsptr->ScriptAsLength) { rqpsptr->ScriptAsPtr = VmGetHeap (rqptr, mrpsptr->ScriptAsLength+1); memcpy (rqpsptr->ScriptAsPtr, mrpsptr->ScriptAsPtr, mrpsptr->ScriptAsLength+1); } else rqpsptr->ScriptAsPtr = NULL; } /* others can be applied in an independent function */ MapSetPath (rqptr, rqpsptr, mrpsptr); /* when mapping VMS to URL then ignore all BUT "pass" rules */ if (!MapPathToVms && mrptr->RuleType != MAPURL_RULE_PASS) continue; /* must be immediately detected and actioned by the calling routine */ if (rqpsptr->Http2ToHttp11) return ("\0\0"); if (rqpsptr->ClientAddress && rqpsptr == &rqptr->rqPathSet) { if (rqpsptr->ClientAddress == MAPURL_CLIENT_RESET) MapUrl_ResetClientAddress (rqptr); else if (cptr = MapUrl_SetClientAddress (rqptr)) return (cptr); } } /***************/ /* end SETings */ /***************/ if (rqpsptr->PathOds) { /* path file-system has been set, perhaps to turn off ODS-5! */ if (rqpsptr->PathOds == MAPURL_PATH_ODS_0) PathOds = rqptr->PathOds = rqpsptr->PathOds = 0; else PathOds = rqptr->PathOds = rqpsptr->PathOds; } else { /* set from the $GETDVI volume file-system */ PathOds = rqptr->PathOds = mrptr->ResultPathOds; } if (PathOdsPtr) *PathOdsPtr = PathOds; if (MapPathToVms) { /***************************/ /* mapping from URL to VMS */ /***************************/ switch (mrptr->RuleType) { case MAPURL_RULE_EXEC : case MAPURL_RULE_UXEC : /*******************/ /* EXEC/UXEC rule */ /*******************/ if (!WildStringCount) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); /* first get the script component from the request */ tptr = mrptr->TemplatePtr; zptr = (sptr = ScriptPtr) + SizeOfScriptPtr; if (mrptr->RuleType == MAPURL_RULE_UXEC) { if (!rqpsptr->ScriptAsPtr || rqpsptr->ScriptAsPtr[0] != '~') { /* hmmm, not enabled with a mapping rule!! */ return (MsgFor(rqptr,MSG_GENERAL_DISABLED)-1); } /* first insert the /~username component */ while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++; if (*tptr) tptr++; cptr = WildString[1]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr = WildString[2]; } else if (SAME2(tptr,'/~')) { /* mapping a DECnet username based script */ while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++; while (*tptr == '*') tptr++; cptr = WildString[1]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr = WildString[2]; } else { /* mapping a vanilla exec rule */ cptr = WildString[1]; } while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++; while (*tptr && SAME2(tptr,'**')) tptr++; if (SAME2(tptr,'*.')) { /* EXECing a file type here */ ExecFileType = true; while (*cptr && sptr < zptr) *sptr++ = *cptr++; /* now get the script file type (e.g. ".cgi") */ tptr++; while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++; } else { /* EXECing a directory here */ ExecFileType = false; while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; } if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; if (mrptr->RuleType == MAPURL_RULE_UXEC) { /* "append" the username to the circumflex */ rqpsptr->ScriptAsPtr = sptr = VmGetHeap (rqptr, strlen(WildString[1])+2); *sptr++ = '~'; for (cptr = WildString[1]; *cptr; *sptr++ = to_upper(*cptr++)); *sptr = '\0'; } /* now get what it's mapped into (the more complex bit ;^) */ rptr = mrptr->ResultPtr; if (*rptr == '(') { /* run-time, e.g. exec /plbin/@ (ht_exe:pl.exe)/plbin/@ */ rptr++; zptr = (sptr = RunTimePtr) + SizeOfRunTimePtr; while (*rptr && *rptr != ')' && sptr < zptr) *sptr++ = *rptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; if (*rptr) rptr++; } zptr = (sptr = Scratch) + sizeof(Scratch); if (mrptr->RuleType == MAPURL_RULE_UXEC) { /* begin with the mapped user instead of the /~username */ if (rqpsptr->PathOds) uptr = MapUserName (rqptr, WildString[1], NULL); else { uptr = MapUserName (rqptr, WildString[1], &PathOds); rqptr->PathOds = PathOds; if (PathOdsPtr) *PathOdsPtr = PathOds; } if (!uptr[0]) return (uptr); /* copy in the now-mapped username */ while (*uptr && sptr < zptr) *sptr++ = *uptr++; /* skip over the equivalent in the result (i.e. "/~*") */ while (*rptr && *rptr != '*') rptr++; if (*rptr) rptr++; /* skip the first wildcarded string (username) */ cptr = WildString[widx=2]; } else if (SAME2(mrptr->TemplatePtr,'/~')) { /* e.g. exec /~@/cgi-bin/@ /0""::/web/user/@/cgi-bin/@ i.e. the user part must be prepended to the script part. */ while (*rptr && *rptr != '*' && sptr < zptr) { if (*rptr != '\"' || !SAME4(rptr,'\"\"::')) { *sptr++ = *rptr++; continue; } /* mapping a DECnet username based script */ *sptr++ = *rptr++; cptr = WildString[1]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; } while (*rptr == '*') rptr++; cptr = WildString[1]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr = WildString[widx=2]; } else { /* vanilla script exec */ if (MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); for (cptr = MapRootPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); } cptr = WildString[widx=1]; } /* now the leading script-location part of the result */ while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++; while (*rptr == '*') rptr++; if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "!&Z", cptr); if (ExecFileType) { /* EXECing a file type here, script name (full buffer) */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr = WildString[++widx]; /* now get the script file type (e.g. ".cgi") */ while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++; } else { /* EXECing a directory here, script name */ while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; } if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; /* generate the VMS script name */ MapOdsUrlToVms (Scratch, ScriptVmsPtr, SizeOfScriptVmsPtr, RmsSubChar, MapEllipsis, PathOds); /* indicate CGIplus script via leading '+' instead of '/' */ if (mrptr->IsCgiPlusScript && (!RunTimePtr || !RunTimePtr[0])) ScriptPtr[0] = '+'; /* get all including and after the first slash as the new path */ zptr = (sptr = DerivedPathBuffer) + sizeof(DerivedPathBuffer); rqptr->MetaConMappedPtr = sptr; /* continue copying from the wildcard string */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr++ = '\0'; TotalLength = sptr - DerivedPathBuffer; if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "E/UXEC !&Z !&Z !&Z", ScriptPtr, ScriptVmsPtr, DerivedPathBuffer); if (!rqpsptr->MapEmpty && !DerivedPathBuffer[0]) return ("\0\0"); if (rqpsptr->MapOnce) { /* convert the URL-style to a VMS-style specification */ MapOdsUrlToVms (DerivedPathBuffer, VmsPtr, SizeOfVmsPtr, RmsSubChar, MapEllipsis, PathOds); if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "E/UXEC !&Z !&Z", DerivedPathBuffer, VmsPtr); return (DerivedPathBuffer); } /* restarting at first rule map the path derived from the script */ MetaConPass++; if (rqptr) { rqptr->MetaConPass = MetaConPass; rqptr->MetaConScriptPtr = ScriptPtr; } *VmsPtr = '\0'; PathPtr = DerivedPathBuffer; RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT; MapEllipsis = rqpsptr->MapEllipsis = false; MetaConParseReset (MetaGlobalMappingPtr, true); continue; case MAPURL_RULE_FAIL : /*************/ /* FAIL rule */ /*************/ if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "FAIL !&Z", PathPtr); return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1); case MAPURL_RULE_MAP : /************/ /* MAP rule */ /************/ /* place this in a buffer so not to overwrite original path */ zptr = (pptr = PathPtr = PathBuffer+1) + sizeof(PathBuffer)-1; rqptr->MetaConMappedPtr = pptr; widx = 1; /* scan through the result string */ rptr = mrptr->ResultPtr; while (*rptr) { while (*rptr && *rptr != '*' && pptr < zptr) *pptr++ = *rptr++; if (!*rptr || pptr >= zptr) break; /* a wildcard asterisk, substitute from original path */ while (*rptr == '*') rptr++; if (*rptr == '\'') { /* specific wildcard component, e.g. "*'1" */ rptr++; if (isdigit(*rptr)) { idx = *rptr++ - '0'; cptr = WildString[idx]; } else cptr = ""; } else if (widx < REGEX_PMATCH_MAX) cptr = WildString[widx++]; else cptr = ""; while (*cptr && pptr < zptr) *pptr++ = *cptr++; } if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; /* continue with the substituted URL mapping */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MAP !&Z", PathPtr); continue; case MAPURL_RULE_PASS : case MAPURL_RULE_REDIRECT : case MAPURL_RULE_USER : /****************************/ /* PASS/REDIRECT/USER rules */ /****************************/ cptr = WildString[widx=1]; rptr = mrptr->ResultPtr; zptr = (pptr = PathBuffer+1) + sizeof(PathBuffer)-1; /* if there is no result to map just pass back the original path */ if (!*rptr) { if (mrptr->RuleType == MAPURL_RULE_PASS && MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); for (cptr = MapRootPtr; *cptr && pptr < zptr; *pptr++ = *cptr++); for (cptr = PathPtr; *cptr && pptr < zptr; *pptr++ = *cptr++); if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; pptr = PathBuffer; } else pptr = PathPtr; if (*pptr != '/') { /* doesn't look like file-system, don't bother translating */ return (PathPtr); } /* convert the URL-style to a VMS-style specification */ MapOdsUrlToVms (pptr, VmsPtr, SizeOfVmsPtr, RmsSubChar, MapEllipsis, PathOds); if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "PASS !&Z !&Z", PathPtr, VmsPtr); return (PathPtr); } /* if redirect rule leave room for the redirection indicator */ if (mrptr->RuleType == MAPURL_RULE_REDIRECT) pptr++; if (mrptr->RuleType == MAPURL_RULE_USER) { /* begin with the mapped user instead of the /~username */ if (rqpsptr->PathOds) uptr = MapUserName (rqptr, WildString[1], NULL); else { uptr = MapUserName (rqptr, WildString[1], &PathOds); rqptr->PathOds = PathOds; if (PathOdsPtr) *PathOdsPtr = PathOds; } if (!uptr[0]) return (uptr); /* copy in the-now mapped username */ while (*uptr && pptr < zptr) *pptr++ = *uptr++; /* skip over the equivalent in the result (i.e. "/~*") */ while (*rptr && *rptr != '*') rptr++; if (*rptr) rptr++; /* skip first wildcard string (username) */ cptr = WildString[widx=2]; } if (mrptr->RuleType == MAPURL_RULE_PASS || mrptr->RuleType == MAPURL_RULE_REDIRECT) { if (!WildStringCount) { if (cptr = SysTrnLnm (mrptr->ResultPtr)) { /****************************/ /* use logical name content */ /****************************/ if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "TRLNM !AZ \"!AZ\"", mrptr->ResultPtr, cptr); if (mrptr->RuleType == MAPURL_RULE_PASS) { zptr = (sptr = VmsPtr) + SizeOfVmsPtr; while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; zptr = (sptr = PathBuffer+1) + sizeof(PathBuffer)-1; cptr = mrptr->ResultPtr; } else { zptr = (sptr = PathBuffer+1) + sizeof(PathBuffer)-1; /* the redirection indicator */ *sptr++ = '\1'; } while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; return (PathBuffer+1); } } } if (mrptr->RuleType == MAPURL_RULE_PASS) { /* scan through the result string */ if (rptr[0] == '\"' || rptr[0] == '\'') if (isdigit(rptr[1]) && isdigit(rptr[2]) && isdigit(rptr[3]) && (!rptr[4] || rptr[4] == ' ')) StatusCodeMapping = true; else StatusCodeMapping = false; else if (isdigit(rptr[0]) && isdigit(rptr[1]) && isdigit(rptr[2]) && (!rptr[3] || rptr[3] == ' ')) StatusCodeMapping = true; else StatusCodeMapping = false; if (StatusCodeMapping) while (*rptr && pptr < zptr) *pptr++ = *rptr++; } else StatusCodeMapping = false; if (!StatusCodeMapping) { if (mrptr->RuleType == MAPURL_RULE_PASS && MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); for (cptr = MapRootPtr; *cptr && pptr < zptr; *pptr++ = *cptr++); } /* map wildcard asterisks */ while (*rptr) { while (*rptr && *rptr != '*' && pptr < zptr) *pptr++ = *rptr++; if (!*rptr) break; /* wildcard asterisk, substitute from the wildcard buffer */ while (*rptr == '*') rptr++; if (*rptr == '\'') { /* specific wildcard component, e.g. "*'1" */ rptr++; if (isdigit(*rptr)) { idx = *rptr++ - '0'; cptr = WildString[idx]; } else cptr = ""; } else if (widx < REGEX_PMATCH_MAX) cptr = WildString[widx++]; else cptr = ""; if (mrptr->RuleType == MAPURL_RULE_REDIRECT) { /* URL-encode wildcard portions from the path */ while (*cptr && pptr < zptr) { for (eptr = FaoUrlEncodeTable[*(unsigned char*)cptr]; *eptr && pptr < zptr; *pptr++ = *eptr++); cptr++; } } else while (*cptr && pptr < zptr) *pptr++ = *cptr++; if (pptr >= zptr) break; } } if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; if (mrptr->RuleType == MAPURL_RULE_PASS || mrptr->RuleType == MAPURL_RULE_USER) { /* if status code mapping return leading null char */ if (StatusCodeMapping) { PathBuffer[0] = '\0'; return (PathBuffer); } if (PathBuffer[1] != '/') { /* doesn't look like file-system, don't bother translating */ return (PathBuffer+1); } /* convert the URL-style to a VMS-style specification */ MapOdsUrlToVms (PathBuffer+1, VmsPtr, SizeOfVmsPtr, RmsSubChar, MapEllipsis, PathOds); if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "PASS !&Z !&Z", PathPtr, VmsPtr); return (PathBuffer+1); } else { if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "REDIRECT !&Z !&Z", PathPtr, VmsPtr); /* indicate it's a redirect */ PathBuffer[1] = '\1'; return (PathBuffer+1); } case MAPURL_RULE_SCRIPT : /***************/ /* SCRIPT rule */ /***************/ tptr = mrptr->TemplatePtr; zptr = (pptr = ScriptPtr) + SizeOfScriptPtr; while (*tptr && *tptr != '*' && pptr < zptr) *pptr++ = *tptr++; if (pptr > ScriptPtr && pptr[-1] == '/') pptr--; if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; rptr = mrptr->ResultPtr; if (*rptr == '(') { /* run-time, e.g. exec /plbin/@ (ht_exe:pl.exe)/plbin/@ */ rptr++; zptr = (sptr = RunTimePtr) + SizeOfRunTimePtr; while (*rptr && *rptr != ')' && sptr < zptr) *sptr++ = *rptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; if (*rptr) rptr++; } zptr = (sptr = Scratch) + sizeof(Scratch); if (MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); for (cptr = MapRootPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); } while (*rptr && *rptr != '*' && !SAME2(rptr,'/*') && sptr < zptr) *sptr++ = *rptr++; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *sptr = '\0'; /* convert the URL-format script to VMS specification */ MapOdsUrlToVms (Scratch, ScriptVmsPtr, SizeOfScriptVmsPtr, RmsSubChar, MapEllipsis, PathOds); /* indicate CGIplus script via leading '+' instead of '/' */ if (mrptr->IsCgiPlusScript && (!RunTimePtr || !RunTimePtr[0])) ScriptPtr[0] = '+'; /* get wildcard matched second section of path as new path */ zptr = (sptr = DerivedPathBuffer) + sizeof(DerivedPathBuffer); rqptr->MetaConMappedPtr = sptr; if (rqpsptr->MapOnce && MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); for (cptr = MapRootPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); } while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++; cptr = WildString[1]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '\0'; if (sptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); TotalLength = sptr - DerivedPathBuffer; if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "SCRIPT !&Z !&Z !&Z", ScriptPtr, ScriptVmsPtr, DerivedPathBuffer); if (!rqpsptr->MapEmpty && !DerivedPathBuffer[0]) return ("\0\0"); if (rqpsptr->MapOnce) { /* convert the URL-style to a VMS-style specification */ MapOdsUrlToVms (DerivedPathBuffer, VmsPtr, SizeOfVmsPtr, RmsSubChar, MapEllipsis, PathOds); if (WATCH_MODULE_DETAIL) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "SCRIPT !&Z !&Z", DerivedPathBuffer, VmsPtr); return (DerivedPathBuffer); } /* restarting at first rule map the path derived from the script */ MetaConPass++; if (rqptr) { rqptr->MetaConPass = MetaConPass; rqptr->MetaConScriptPtr = ScriptPtr; } *VmsPtr = '\0'; PathPtr = DerivedPathBuffer; RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT; MapEllipsis = rqpsptr->MapEllipsis = false; MetaConParseReset (MetaGlobalMappingPtr, true); continue; case MAPURL_RULE_PROTECT : /****************/ /* PROTECT rule */ /****************/ if (MetaConPass == 1) { /* pass one protects the full path or script component */ if (mrptr->ResultLength) { rqptr->rqAuth.Protect1Ptr = VmGetHeap (rqptr, mrptr->ResultLength+1); rqptr->rqAuth.Protect1Length = mrptr->ResultLength; memcpy (rqptr->rqAuth.Protect1Ptr, mrptr->ResultPtr, mrptr->ResultLength+1); } else { /* empty result string cancels any previous authorization */ rqptr->rqAuth.Protect1Ptr = NULL; rqptr->rqAuth.Protect1Length = 0; } } else { /* pass two protects any path following the script component */ if (mrptr->ResultLength) { rqptr->rqAuth.Protect2Ptr = VmGetHeap (rqptr, mrptr->ResultLength+1); rqptr->rqAuth.Protect2Length = mrptr->ResultLength; memcpy (rqptr->rqAuth.Protect2Ptr, mrptr->ResultPtr, mrptr->ResultLength+1); } else { /* empty result string cancels any previous authorization */ rqptr->rqAuth.Protect1Ptr = NULL; rqptr->rqAuth.Protect1Length = 0; } } continue; case MAPURL_RULE_SET : /************/ /* SET rule */ /************/ /* path SETings have already been applied */ continue; } } else { /***************************/ /* mapping from VMS to URL */ /***************************/ /* REVERSE maps a VMS "result" to a "template" :^) */ cptr = WildString[idx = 1]; if (MapRootPtr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "ROOT !AZ", MapRootPtr); while (MapRootLength-- && *cptr) cptr++; } zptr = (pptr = PathPtr) + SizeOfPathPtr; tptr = mrptr->TemplatePtr; /* scan through the template string */ while (*tptr) { while (*tptr && *tptr != '*' && pptr < zptr) *pptr++ = *tptr++; if (!*tptr) break; /* a wildcard asterisk, substitute from result path */ while (*tptr == '*') tptr++; if (*cptr) while (*cptr && pptr < zptr) *pptr++ = *cptr++; /* three lines fix 05-APR-2018 longstanding bug */ if (!*tptr) break; if (idx < REGEX_PMATCH_MAX) idx++; cptr = WildString[idx]; } if (pptr >= zptr) return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); *pptr = '\0'; return (PathPtr); } } /***********************/ /* a mapping not found */ /***********************/ if (WatchThisOne) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "END of mapping rules"); if (MapPathToVms) return (MsgFor(rqptr,MSG_MAPPING_DENIED_DEFAULT)-1); else return (MAPURL_NO_REVERSE_PATH); } /*****************************************************************************/ /* Load mapping rules into meta-config structure. */ int MapUrl_ConfigLoad (META_CONFIG **MetaConPtrPtr) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigLoad()"); status = MetaConLoad (MetaConPtrPtr, CONFIG_MAP_FILE_NAME, &MapUrl_ConfigLoadCallBack, true, true); if (*MetaConPtrPtr == MetaGlobalMappingPtr) { /* server startup/reload */ MetaConStartupReport (MetaGlobalMappingPtr, "MAP"); if (VMSnok (status)) exit (status); } return (status); } /*****************************************************************************/ /* Called by MetaConUnload() to free resources allocated during mapping rule configuration. */ MapUrl_ConfigUnload (META_CONFIG *mcptr) { int status; MAPPING_META *mmptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigUnload()"); if (mcptr->MappingMetaPtr) { if (mcptr->MappingMetaPtr == MappingMetaPtr) { memset (MappingMetaPtr, 0, sizeof(MAPPING_META)); MappingMetaPtr = NULL; } else VmFree (mcptr->MappingMetaPtr, FI_LI); mcptr->MappingMetaPtr = NULL; } } /*****************************************************************************/ /* Called by MetaConUnload() callback for each line's associated data, basically to check for a regular expression structure and free it if present, then just dispose of the line data itself. */ MapUrl_ConfigUnloadLineData (void *LineDataPtr) { int status; MAP_RULE_META *mrptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigUnloadLineData()"); mrptr = (MAP_RULE_META*)LineDataPtr; if (mrptr->RegexPregTemplate.buffer) regfree (&mrptr->RegexPregTemplate); VmFree (mrptr, FI_LI); } /*****************************************************************************/ /* For each non-meta-config directive line read by MetaConLoad() this function is called to parse the line text's contents and to configure the private data structure associated with each rule. */ BOOL MapUrl_ConfigLoadCallBack (META_CONFIG *mcptr) { static char Ods5Disabled [] = "ODS-5 processing CLI disabled", Ods5Enabled [] = "ODS-5 processing enabled", Ods5NotSupported [] = "ODS-5 not supported", ProblemAcceptLang [] = "Set path \'accept=lang\' parameter problem", ProblemConditional [] = "Conditional problem", ProblemConfused [] = "Cannot understand this one at all", ProblemHostNameLookup [] = "Cannot resolve host name !AZ, !&m", ProblemNameValue [] = "Set path \'name=value\' pair problem", ProblemNotAllowed [] = "Rule cannot be used in [ConfigFile]", ProblemOverflow [] = "Storage overflow", ProblemProtect [] = "Protect path problem", ProblemProxyHeader [] = "Proxy header exceeded maximum", ProblemRegex [] = "Regex: !AZ", ProblemResultPath [] = "Problem with \'result\' path", ProblemResultRequired [] = "Requires a \'result\'", ProblemResultStatus [] = "Rule \'result\' cannot be a status", ProblemRmsSubstitution [] = "RMS substitution character", ProblemSetPath [] = "Set path problem", ProblemSpecifiedWildcardCannot [] = "Cannot use \'specified\' wildcards with rule", ProblemSpecifiedWildcardMix [] = "Mix of \'specified\' and non-\'specified\' wildcards", ProblemSpecifiedWildcardRange [] = "Value of \'specified\' wildcard out-of-range", ProblemTemplateRequired [] = "Requires a \'template\'", ProblemThrottleValues [] = "Successive dependent throttle values must be larger", ProblemWildcardMapping [] = "Wildcard mapping problem", ProblemTooManyWildcard [] = "Too many wildcards"; BOOL AllowedInConfigFile, ConfigProblem, IsCgiPlusScript, PathSet, VirtualServerUnknown; int length, status, ConfigFileIndex, ResultPathOds, RuleType, TemplateWildcardCount, ResultWildcardCount, ResultSpecifiedWildcardCount; unsigned short Length; char *cptr, *sptr, *zptr, *ConditionalPtr, *ResultPtr, *RulePtr, *SetPathPtr, *TemplatePtr; char Name [256], DiskDevice [64+1], Value [1024]; MAPPING_META *mmptr; MAP_RULE_META *mrptr; MAP_SET_META *mrpsptr; METACON_LINE *mclptr; regex_t RegexPreg; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) { WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigLoadCallBack() !&F !&X", &MapUrl_ConfigLoadCallBack, mcptr); if (WATCH_MODULE(WATCH_MOD__DETAIL)) { mclptr = mcptr->ParsePtr; WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z !&Z\n", mclptr, mclptr->Size, mclptr->Token, mclptr->Number, mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr, mclptr->InlineTextPtr); } } /* get a pointer to the current "line" */ mclptr = mcptr->ParsePtr; /* if this is during server startup/reload set the global service pointer */ if (mcptr == MetaGlobalMappingPtr) mmptr = mcptr->MappingMetaPtr = MappingMetaPtr = &MappingMeta; else /* if a report then conjure one up through quantum mechanics */ if (!mcptr->MappingMetaPtr) mmptr = mcptr->MappingMetaPtr = VmGet (sizeof(MAPPING_META)); else /* not the first time through */ mmptr = mcptr->MappingMetaPtr; if (mclptr->Token == METACON_TOKEN_PRE) { /******************/ /* pre-initialize */ /******************/ MapUrlPathOds5 = OdsExtended = false; return (true); } if (mclptr->Token == METACON_TOKEN_POST) { /****************/ /* post-process */ /****************/ /* if global service pointer, during server startup or reload */ if (mcptr == MetaGlobalMappingPtr) { /* if no ODS5 paths then EFS can be disabled */ #ifdef ODS_EXTENDED if (CliOdsExtendedDisabled) { OdsExtended = false; MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5Disabled); } else #endif /* ODS_EXTENDED */ if (CliOdsExtendedEnabled || MapUrlPathOds5) { #ifdef ODS_EXTENDED if (SysInfo.VersionInteger >= 720) { OdsExtended = true; MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5Enabled); } else #endif /* ODS_EXTENDED */ { OdsExtended = false; MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5NotSupported); } } else OdsExtended = false; /* reset the username cache */ MapUserNameCache (NULL, NULL, NULL, NULL); /* initialize the throttled paths */ ThrottleInit (); } return (true); } /***********/ /* process */ /***********/ /* if it's not text/inline then mapping's not interested in it */ if (mclptr->Token != METACON_TOKEN_TEXT && !mclptr->InlineTextPtr) return (true); if (mclptr->InlineTextPtr) cptr = mclptr->InlineTextPtr; else cptr = mclptr->TextPtr; for (sptr = cptr; *sptr; sptr++); /* allocate memory for the structure plus the null terminated strings */ mrptr = (MAP_RULE_META*)VmGet (sizeof(MAP_RULE_META)+(sptr-cptr+1)); /* copy to storage the configuration string */ sptr = &mrptr->Storage; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; /* point to the MAP_RULE_META */ mrpsptr = &mrptr->mpPathSet; AllowedInConfigFile = ConfigProblem = IsCgiPlusScript = PathSet = false; /********/ /* rule */ /********/ for (cptr = &mrptr->Storage; *cptr && ISLWS(*cptr); cptr++); RulePtr = cptr; if (StringSliceValue (&cptr) <= 0) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused); VmFree (mrptr, FI_LI); return (true); } IsCgiPlusScript = false; if (strsame (RulePtr, "EXEC", -1)) RuleType = MAPURL_RULE_EXEC; else if (strsame (RulePtr, "EXEC+", -1)) { RuleType = MAPURL_RULE_EXEC; IsCgiPlusScript = true; } else if (strsame (RulePtr, "FAIL", -1)) { RuleType = MAPURL_RULE_FAIL; AllowedInConfigFile = true; } else if (strsame (RulePtr, "MAP", -1)) { RuleType = MAPURL_RULE_MAP; AllowedInConfigFile = true; } else if (strsame (RulePtr, "PASS", -1)) { RuleType = MAPURL_RULE_PASS; AllowedInConfigFile = true; } else if (strsame (RulePtr, "REDIRECT", -1)) { RuleType = MAPURL_RULE_REDIRECT; AllowedInConfigFile = true; } else if (strsame (RulePtr, "SCRIPT", -1)) RuleType = MAPURL_RULE_SCRIPT; else if (strsame (RulePtr, "SCRIPT+", -1)) { RuleType = MAPURL_RULE_SCRIPT; IsCgiPlusScript = true; } else if (strsame (RulePtr, "SET", -1)) RuleType = MAPURL_RULE_SET; else if (strsame (RulePtr, "UXEC", -1) || strsame (RulePtr, "UEXEC", -1)) RuleType = MAPURL_RULE_UXEC; else if (strsame (RulePtr, "UXEC+", -1) || strsame (RulePtr, "UEXEC+", -1)) { RuleType = MAPURL_RULE_UXEC; IsCgiPlusScript = true; } else if (strsame (RulePtr, "USER", -1)) RuleType = MAPURL_RULE_USER; else if (strsame (RulePtr, "PROTECT", -1)) { RuleType = MAPURL_RULE_PROTECT; AllowedInConfigFile = true; } else { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused); VmFree (mrptr, FI_LI); return (true); } /* ensure a conditional is not mistaken for a missing template */ if (!*cptr || *cptr == '[' || SAME2(cptr,'![')) { /* there must be a "template" to map from */ MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTemplateRequired); VmFree (mrptr, FI_LI); return (true); } /* extract template (the path the rule is applied to) */ TemplatePtr = cptr; if (StringSliceValue (&cptr) <= 0) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTemplateRequired); VmFree (mrptr, FI_LI); return (true); } /* note the position of an empty string in case none of these! */ ResultPtr = SetPathPtr = ConditionalPtr = RulePtr + strlen(RulePtr); if (RuleType != MAPURL_RULE_SET) { /*********************/ /* path mapping rule */ /*********************/ /* if this doesn't look like a conditional */ if (*cptr && *cptr != '[' && !SAME2(cptr,'![')) { ResultPtr = cptr; if (StringSliceValue (&cptr) <= 0) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused); VmFree (mrptr, FI_LI); return (true); } } } /* if it doesn't look like a conditional then assume it's SET directives */ if (*cptr && *cptr != '[' && !SAME2(cptr,'![')) { /****************/ /* path SETings */ /****************/ while (*cptr) { /* break if this looks like a conditional */ if (*cptr == '[' || SAME2(cptr,'![')) break; SetPathPtr = cptr; if ((length = StringSliceValue (&cptr)) <= 0) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSetPath); VmFree (mrptr, FI_LI); return (true); } if (!MapSetConfig (SetPathPtr, TemplatePtr, mcptr, mrpsptr, &AllowedInConfigFile)) { /* problem with SETing */ VmFree (mrptr, FI_LI); return (true); } PathSet = true; } } if (RuleType == MAPURL_RULE_SET && !PathSet) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSetPath); VmFree (mrptr, FI_LI); return (true); } if (RuleType == MAPURL_RULE_PROTECT) { /****************/ /* protect rule */ /****************/ /* PROTECT rules are always added to the mapping database, whether correct or not, so that even when incorrect the path is still access controlled (and in an incorrect case always denied!) */ sptr = AuthConfigParseProtectRule (NULL, ResultPtr, strlen(ResultPtr)); if (sptr) { MetaConReport (mcptr, METACON_REPORT_ERROR, sptr); ConfigProblem = true; } AuthProtectRule = true; } /* check for optional mapping conditional(s) */ if (*cptr == '[' || SAME2(cptr,'![')) { /***************/ /* conditional */ /***************/ ConditionalPtr = cptr; while (*cptr == '[' || SAME2(cptr,'![')) { while (*cptr == '[' || *cptr == '!') cptr++; while (*cptr && *cptr != ']') { while (ISLWS(*cptr)) cptr++; sptr = cptr; while (*cptr && *cptr != ':' && !ISLWS(*cptr) && *cptr != ']') { if (*cptr == '\\') cptr++; *cptr = to_upper(*cptr); if (*cptr) cptr++; } /* basic check of conditional rule */ if (*sptr == '!') sptr++; if ((!SAME2(sptr,'AC') && !SAME2(sptr,'AL') && !SAME2(sptr,'AS') && !SAME2(sptr,'CA') && !SAME2(sptr,'CK') && !SAME2(sptr,'DR') && !SAME2(sptr,'EX') && !SAME2(sptr,'FO') && !SAME2(sptr,'HH') && !SAME2(sptr,'HM') && !SAME2(sptr,'HO') && !SAME2(sptr,'ME') && !SAME2(sptr,'MP') && !SAME2(sptr,'NO') && !SAME2(sptr,'PA') && !SAME2(sptr,'PI') && !SAME2(sptr,'QS') && !SAME2(sptr,'RC') && !SAME2(sptr,'RF') && !SAME2(sptr,'RQ') && !SAME2(sptr,'RU') && !SAME2(sptr,'SC') && !SAME2(sptr,'SN') && !SAME2(sptr,'SP') && !SAME2(sptr,'ST') && !SAME2(sptr,'UA') && !SAME2(sptr,'VS') && !SAME2(sptr,'XF')) || sptr[2] != ':') { /***********************/ /* conditional problem */ /***********************/ MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConditional); VmFree (mrptr, FI_LI); return (true); } while (*cptr && !ISLWS(*cptr) && *cptr != ']') { if (*cptr == '\\') cptr++; if (*cptr) cptr++; } while (ISLWS(*cptr)) cptr++; } if (*cptr == ']') cptr++; while (ISLWS(*cptr)) cptr++; } /* terminate in the white-space following the conditional(s) */ if (*cptr) *cptr = '\0'; } /**********************/ /* allowed rule check */ /**********************/ if (mclptr->MetaFileType == METACON_TYPE_CONFIG) { if (!AllowedInConfigFile) { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemNotAllowed); VmFree (mrptr, FI_LI); return (true); } } /***************************/ /* rule consistency checks */ /***************************/ if (mrpsptr->RmsSubChar && !isalnum(mrpsptr->RmsSubChar) && mrpsptr->RmsSubChar != '$' && mrpsptr->RmsSubChar != '-' && mrpsptr->RmsSubChar != '_') { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemRmsSubstitution); VmFree (mrptr, FI_LI); return (true); } TemplateWildcardCount = 0; cptr = TemplatePtr; while (*cptr) { if (*cptr++ != '*') continue; TemplateWildcardCount++; /* contiguous count as one! */ while (*cptr == '*') cptr++; } if (TemplateWildcardCount > REGEX_PMATCH_MAX) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTooManyWildcard); VmFree (mrptr, FI_LI); return (true); } memset (&RegexPreg, 0, sizeof(RegexPreg)); if (Config.cfMisc.RegexSyntax && *TemplatePtr == REGEX_CHAR) { cptr = StringRegexCompile (TemplatePtr+1, &RegexPreg); if (cptr) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemRegex, cptr); VmFree (mrptr, FI_LI); return (true); } } ResultWildcardCount = ResultSpecifiedWildcardCount = 0; cptr = ResultPtr; while (*cptr) { if (*cptr++ != '*') continue; ResultWildcardCount++; /* contiguous count as one! */ while (*cptr == '*') cptr++; if (*cptr != '\'') continue; ResultSpecifiedWildcardCount++; cptr++; if (*cptr < '1' || *cptr > '9') { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSpecifiedWildcardRange); VmFree (mrptr, FI_LI); return (true); } } if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "!&Z !&Z !&Z !UL !UL !UL", TemplatePtr, ResultPtr, ConditionalPtr, TemplateWildcardCount, ResultWildcardCount, ResultSpecifiedWildcardCount); ResultPathOds = 0; switch (RuleType) { case MAPURL_RULE_SCRIPT : if (!ResultPtr[0]) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired); VmFree (mrptr, FI_LI); return (true); } if (isdigit(ResultPtr[0])) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus); VmFree (mrptr, FI_LI); return (true); } if (ResultSpecifiedWildcardCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSpecifiedWildcardCannot); VmFree (mrptr, FI_LI); return (true); } if (ResultWildcardCount != TemplateWildcardCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemWildcardMapping); VmFree (mrptr, FI_LI); return (true); } break; case MAPURL_RULE_EXEC : case MAPURL_RULE_UXEC : if (!ResultPtr[0]) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired); VmFree (mrptr, FI_LI); return (true); } if (isdigit(ResultPtr[0])) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus); VmFree (mrptr, FI_LI); return (true); } if (ResultSpecifiedWildcardCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSpecifiedWildcardCannot); VmFree (mrptr, FI_LI); return (true); } if (!ResultWildcardCount || !TemplateWildcardCount || ResultWildcardCount != TemplateWildcardCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemWildcardMapping); VmFree (mrptr, FI_LI); return (true); } break; case MAPURL_RULE_FAIL : break; case MAPURL_RULE_PASS : if (ResultSpecifiedWildcardCount && ResultSpecifiedWildcardCount != ResultWildcardCount) { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemSpecifiedWildcardMix); VmFree (mrptr, FI_LI); return (true); } if (((ResultPtr[0] == '\"' || ResultPtr[0] == '\'') && !isdigit(ResultPtr[0]) && ResultWildcardCount > TemplateWildcardCount) || ((ResultPtr[0] != '\"' && ResultPtr[0] != '\'') && !isdigit(ResultPtr[0]) && ResultWildcardCount > TemplateWildcardCount)) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemWildcardMapping); VmFree (mrptr, FI_LI); return (true); } #ifdef ODS_EXTENDED /* determine the on-disk structure */ zptr = (sptr = DiskDevice) + sizeof(DiskDevice)-1; if (ResultPtr[0]) cptr = ResultPtr; else cptr = TemplatePtr; if (*cptr == '/') cptr++; while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = ':'; *sptr = '\0'; ResultPathOds = OdsVolumeStructure (DiskDevice); #endif /* ODS_EXTENDED */ break; case MAPURL_RULE_MAP : case MAPURL_RULE_REDIRECT : case MAPURL_RULE_USER : if (!ResultPtr[0]) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired); VmFree (mrptr, FI_LI); return (true); } if (isdigit(ResultPtr[0])) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus); VmFree (mrptr, FI_LI); return (true); } if (ResultSpecifiedWildcardCount && ResultSpecifiedWildcardCount != ResultWildcardCount) { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemSpecifiedWildcardMix); VmFree (mrptr, FI_LI); return (true); } if (ResultWildcardCount > TemplateWildcardCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemWildcardMapping); VmFree (mrptr, FI_LI); return (true); } break; case MAPURL_RULE_PROTECT : case MAPURL_RULE_SET : break; default : MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused); VmFree (mrptr, FI_LI); return (true); } /*****************************/ /* throttle integrity checks */ /*****************************/ if (mrpsptr->ThrottleSet && !mrpsptr->ThrottleNoSet) { /* throttle=from,to,resume,busy,t/o-resume,t/o-busy */ if ((mrpsptr->ThrottleTo && mrpsptr->ThrottleTo <= mrpsptr->ThrottleFrom) || (mrpsptr->ThrottleResume && !mrpsptr->ThrottleTo) || (mrpsptr->ThrottleResume && mrpsptr->ThrottleResume <= mrpsptr->ThrottleTo) || (mrpsptr->ThrottleBusy && mrpsptr->ThrottleTo && mrpsptr->ThrottleBusy <= mrpsptr->ThrottleTo) || (mrpsptr->ThrottleBusy && mrpsptr->ThrottleResume && mrpsptr->ThrottleBusy <= mrpsptr->ThrottleResume)) { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemThrottleValues); VmFree (mrptr, FI_LI); return (true); } } /************************************/ /* script=param=(name=value) checks */ /************************************/ if (mrpsptr->ScriptParamsPtr) { /* these will generally end up as DCL symbols for a script */ cptr = mrpsptr->ScriptParamsPtr; for (;;) { status = StringParseNameValue (&cptr, true, Name, sizeof(Name), Value, sizeof(Value)); if (VMSnok (status)) break; } if (status != SS$_ENDOFFILE) { MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemNameValue); VmFree (mrptr, FI_LI); return (true); } } /*********************************/ /* add the rule as the line data */ /*********************************/ /* set the meta-config data */ mclptr->LineDataPtr = mrptr; mclptr->ConfigProblem = ConfigProblem; /* set the rule data */ mrptr->MetaConNumber = mclptr->Number; mrptr->RuleType = RuleType; mrptr->TemplatePtr = TemplatePtr; mrptr->ResultPtr = ResultPtr; mrptr->ResultLength = strlen(ResultPtr); mrptr->ConditionalPtr = ConditionalPtr; mrptr->PathSet = PathSet; mrptr->IsCgiPlusScript = IsCgiPlusScript; #ifdef ODS_EXTENDED if (ResultPathOds) { /* indicate that this path is located on an ODS-5 volume */ mrptr->ResultPathOds = ResultPathOds; /* indicate that at least one mapped volume is ODS-5 */ if (ResultPathOds == MAPURL_PATH_ODS_5) MapUrlPathOds5 = true; } #endif /* ODS_EXTENDED */ if (RegexPreg.buffer) { /* the template was a regular expression, create a compiled version */ StringRegexCompile (TemplatePtr+1, &mrptr->RegexPregTemplate); /* free the temporary compiled version */ regfree (&RegexPreg); } if (mrpsptr->ThrottleSet && !mrpsptr->ThrottleNoSet) { mrpsptr->ThrottleIndex = mmptr->ThrottleIndex++; if (mmptr->ThrottleIndex > mmptr->ThrottleTotal) mmptr->ThrottleTotal = mmptr->ThrottleIndex; } if (mrpsptr->ProxyBindIpAddressLength) { status = TcpIpStringToAddress (mrpsptr->ProxyBindIpAddressPtr, &mrpsptr->ProxyBindIpAddress); if (VMSnok (status)) MetaConReport (mcptr, METACON_REPORT_ERROR, "Could not get bind address for !AZ", mrpsptr->ProxyBindIpAddressPtr); } if (mrpsptr->ProxyChainHostPortLength) { IPADDRESS_ZERO4 (&mrpsptr->ProxyChainIpAddress) status = NetHostNameLookup (mrpsptr->ProxyChainHostPortPtr, 0, NULL, NULL, NULL, &mrpsptr->ProxyChainIpAddress, &mrpsptr->ProxyChainPort); if (!mrpsptr->ProxyChainPort) mrpsptr->ProxyChainPort = DEFAULT_HTTP_PROXY_PORT; if (VMSnok (status)) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemHostNameLookup, mrpsptr->ProxyChainHostPortPtr, status); /* make sure the connection can't succeed */ IPADDRESS_SET_UNUSABLE (&mrpsptr->ProxyChainIpAddress) mrpsptr->ProxyChainPort = 0; } } /* note that we've got an extension-method rule */ if (mrpsptr->MapExtensionMethod) MapUrlExtensionMethod = true; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!&X !UL !&Z !&Z (!UL) !&B !&Z\n", mrptr, mrptr->RuleType, mrptr->TemplatePtr, mrptr->ResultPtr, mrptr->ResultPathOds, mrptr->PathSet, mrptr->ConditionalPtr); return (true); } /*****************************************************************************/ /* Given the current document's URL path and the specified document's URL path, generate a resultant URL path based on whether the specified document's path is absolute or relative the the current document's path. Insufficient space to contain the result path will not crash the function and the result will be set to an empty string. */ int MapUrl_VirtualPath ( char *CurrentPath, char *DocumentPath, char *ResultPath, int ResultPathSize ) { char *cptr, *dptr, *sptr, *tptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_VirtualPath() !&Z !&Z !UL", CurrentPath, DocumentPath, ResultPathSize); if (*(dptr = DocumentPath) == '/' || strstr (DocumentPath, "//")) { /*****************/ /* absolute path */ /*****************/ strzcpy (ResultPath, dptr, ResultPathSize); zptr = (sptr = ResultPath) + ResultPathSize; while (*dptr && sptr < zptr) *sptr++ = *dptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; return (sptr - ResultPath); } /* step over any "relative to this directory" syntax ("./") */ if (dptr[0] == '.' && dptr[1] == '/') dptr += 2; if (*dptr != '.') { /*****************/ /* inferior path */ /*****************/ zptr = (sptr = tptr = ResultPath) + ResultPathSize; cptr = CurrentPath; while (*cptr && sptr < zptr) { if (*cptr == '/') tptr = sptr+1; *sptr++ = *cptr++; } sptr = tptr; while (*dptr && sptr < zptr) *sptr++ = *dptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; return (sptr - ResultPath); } /*****************/ /* relative path */ /*****************/ zptr = (sptr = tptr = ResultPath) + ResultPathSize; cptr = CurrentPath; while (*cptr && sptr < zptr) { if (*cptr == '/') tptr = sptr; *sptr++ = *cptr++; } sptr = tptr; /* loop, stepping back one level for each "../" in the document path */ while (dptr[0] == '.' && dptr[1] == '.' && dptr[2] == '/') { dptr += 3; if (sptr > ResultPath) sptr--; while (sptr > ResultPath && *sptr != '/') sptr--; } if (sptr > ResultPath && sptr < zptr) sptr++; else if (sptr < zptr) *sptr++ = '/'; while (*dptr && sptr < zptr) *sptr++ = *dptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; return (sptr - ResultPath); } /*****************************************************************************/ /* Modify the client's IP address data using the source indicated by the path setting. Used to support a transparent load balancer/accelerator/proxy upstream from the server while retaining the original client address data. Return a NULL pointer to indicate success. Return an error string if it fails for some reason (e.g. header is not present). A fail should abort mapping and report the error. */ char* MapUrl_SetClientAddress (REQUEST_STRUCT *rqptr) { int ClientAddress; char *cptr, *hptr, *sptr, *zptr; char strbuf [256]; CLIENT_STRUCT *clptr; IPADDRESS IpAddress; REQUEST_PATHSET *rqpsptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_SetClientAddress()"); /* do not reproduce again if multiple client data mappings */ if (rqptr->ClientPtr->SetClientAddress) clptr = rqptr->ClientPtr; else { /* reproduce the existing client data and then use that */ clptr = (CLIENT_STRUCT*)VmGetHeap (rqptr, sizeof(CLIENT_STRUCT)); memcpy (clptr, &rqptr->ClientPtr, sizeof(CLIENT_STRUCT)); rqptr->ClientResetPtr = rqptr->ClientPtr; rqptr->ClientPtr = clptr; } rqpsptr = &rqptr->rqPathSet; ClientAddress = rqpsptr->ClientAddress; rqpsptr->ClientAddress = 0; if (ClientAddress == clptr->SetClientAddress && ClientAddress != MAPURL_CLIENT_LITERAL) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT already set to this!!"); return (NULL); } if (ClientAddress == MAPURL_CLIENT_XFORWARDEDFOR || ClientAddress == MAPURL_CLIENT_IF_XFORWARDEDFOR) { cptr = rqptr->rqHeader.XForwardedForPtr; hptr = "X-Forwarded-For:"; } else if (ClientAddress == MAPURL_CLIENT_FORWARDED || ClientAddress == MAPURL_CLIENT_IF_FORWARDED) { cptr = rqptr->rqHeader.ForwardedPtr; hptr = "Forwarded:"; } else if (ClientAddress == MAPURL_CLIENT_LITERAL) { /* really just for ease of testing */ cptr = rqpsptr->ClientAddressLiteralPtr; hptr = "X-Literal:"; } else { ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1); } if (!cptr) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT no !AZ", hptr); if (ClientAddress == MAPURL_CLIENT_IF_XFORWARDEDFOR || ClientAddress == MAPURL_CLIENT_IF_FORWARDED) return (NULL); return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1); } /* parse out the leading (IP address) string representing the client */ zptr = (sptr = strbuf) + sizeof(strbuf)-1; while (ISLWS(*cptr)) cptr++; if (MATCH4(cptr,"for=")) cptr += 4; if (*cptr == '\"') for (cptr++; *cptr && *cptr != '\"' && sptr < zptr; *sptr++ = *cptr++); else while (*cptr && *cptr != ',' && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (VMSnok (TcpIpStringToAddress (strbuf, &IpAddress))) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT address error !AZ !AZ", hptr, strbuf); return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1); } /* when not multiple client data mapping */ if (!clptr->SetClientAddress) { /* buffer the TCP connection data (see MapUrl_ResetClientData()) */ IPADDRESS_COPY (&clptr->UpstreamIpAddress, &clptr->IpAddress); strzcpy (clptr->UpstreamIpAddressString, &clptr->IpAddressString, sizeof(clptr->UpstreamIpAddressString)); strzcpy (clptr->UpstreamHostName, clptr->Lookup.HostName, sizeof(clptr->UpstreamHostName)); clptr->UpstreamHostNameLength = clptr->Lookup.HostNameLength; } /* rewrite the client address data */ clptr->SetClientAddress = ClientAddress; IPADDRESS_COPY (&clptr->IpAddress, &IpAddress); /* recreate a "clean" IP address string from the IP address above */ cptr = TcpIpAddressToString (&IpAddress, 0); strzcpy (clptr->IpAddressString, cptr, sizeof(clptr->IpAddressString)); strzcpy (clptr->Lookup.HostName, cptr, sizeof(clptr->Lookup.HostName)); clptr->Lookup.HostNameLength = strlen(cptr); if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "!AZ !&I !AZ !AZ -> !&I !AZ !AZ", hptr, &clptr->UpstreamIpAddress, clptr->UpstreamIpAddressString, clptr->UpstreamHostName, &clptr->IpAddress, &clptr->IpAddressString, clptr->Lookup.HostName); if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT !AZ !AZ", hptr, cptr); if (WATCH_NOT_ONE_SHOT(Watch.Category)) WatchFilterClientService (rqptr); return (NULL); } /*****************************************************************************/ /* Undo the client address mapping performed by MapUrl_SetClientAddress() above by simply pointing back using the saved original. */ void MapUrl_ResetClientAddress (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_ResetClientAddress()"); rqptr->ClientPtr = rqptr->ClientResetPtr; } /*****************************************************************************/ /* Guarantee access to certain paths under certain conditions. */ char* MapUrl_GuaranteeAccess ( REQUEST_STRUCT *rqptr, char *PathBeingMapped ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_GuaranteeAccess() !&Z", PathBeingMapped); if (AuthPromiscuous || HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond) { /* guarantee access to the administration menu */ if (!strsame (PathBeingMapped, HTTPD_ADMIN, sizeof(HTTPD_ADMIN)-1) && !strsame (PathBeingMapped, WASD_ROOT_LOCAL, sizeof(WASD_ROOT_LOCAL)-1) && !strsame (PathBeingMapped, HT_ROOT_LOCAL, sizeof(HT_ROOT_LOCAL)-1)) return (NULL); if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "!AZ guarantees access to !AZ", AuthPromiscuous ? "PROMISCUOUS" : "SKELKEY", PathBeingMapped); return (PathBeingMapped); } return (NULL); } /*****************************************************************************/ /* A server administration report on the server's mapping rules. This function just wraps the reporting function, loading a temporary database if necessary for reporting from the rule file. */ MapUrl_Report ( REQUEST_STRUCT *rqptr, BOOL UseServerDatabase, char *VirtualService ) { int status; META_CONFIG *mcptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_Report() !UL !&Z", UseServerDatabase, VirtualService); if (UseServerDatabase) { /* use mapping rules already loaded into the server */ MapUrl_ReportNow (rqptr, true, MetaGlobalMappingPtr, VirtualService); } else { /* load temporary set of rules from mapping file */ status = MapUrl_ConfigLoad (&mcptr); if (VMSnok (status)) { /* severe error reported */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI); } else MapUrl_ReportNow (rqptr, false, mcptr, VirtualService); MetaConUnload (&mcptr, &MapUrl_ConfigUnloadLineData); } AdminEnd (rqptr); } /*****************************************************************************/ /* Return a report on the HTTPd server's mapping rules ... now! */ MapUrl_ReportNow ( REQUEST_STRUCT *rqptr, BOOL UseServerDatabase, META_CONFIG *mcptr, char *VirtualService ) { static char BeginRules [] = "

Mapping Rules

\n"; static char BeginTable [] = "

\n\ \n\
";

   static char  CommentFao [] = "!AZ!AZ\n";

   static char  NutherFileFao [] = "!AZ!AZ\n";

   static char  RuleFao [] =
"!AZ!&?\r\r!AZ  !&;AZ!&@!&@!&@!&?\r\r\n";

   static char  NonRuleFao [] =
"!AZ!&?\r\r!#AZ  !&;AZ!&?\r\r\n";

   static char  ProblemFao [] = "!AZ!&;AZ\n";

   static char  RuleIncludeFao [] = "!AZ!AZ\n";

   static char  RuleVirtualFao [] =
"!AZ!&?\r\r[[!&;AZ]]!&?\r\r\n";

   static char  RuleImplicitVirtualFao [] =
"!AZ[[*:*]]\n";

   static char  VersionFao [] = "!AZ[[!AZ]]\n";

   static char  UserMappingFao [] =
"!AZ\
----\
\n\
\n\

User Mapping Cache

\n\

\n\ \ \ \ \ \ \ \ \ \n\ \n"; static char EntryFao [] = "\ \ \ \ \ \ \ \ \n"; static char EmptyUserMappingFao [] = "\n"; static char EndPageFao [] = "
NamePathODSHitsLastReuse
!4ZL!AZ  !AZ  !AZ  !UL  !20%D  !UL
0000empty
\n\ \n\ \n\ \n"; BOOL FinalRule, ThisVirtualService; int status, EntryCount, InConfigLevel; unsigned long FaoVector [32]; unsigned long *vecptr; unsigned short Length; char *cptr; char Buffer [MAP_BUFFER_SIZE], RmsSubChar [2], TimeString [32]; LIST_ENTRY *leptr; MAP_RULE_META *mrptr; MAP_URL_USER_ENTRY *ucptr; METACON_LINE *mclptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_ReportNow() !&B !&Z", UseServerDatabase, VirtualService); AdminPageTitle (rqptr, "Path Mapping"); AdminMetaConReport (rqptr, mcptr, MetaGlobalMappingPtr); AdminMetaConSource (rqptr, mcptr, MetaGlobalMappingPtr); /*****************/ /* mapping rules */ /*****************/ status = FaolToNet (rqptr, BeginRules, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); AdminVirtualServiceForm (rqptr, ADMIN_REPORT_MAPPING, VirtualService, UseServerDatabase); status = FaolToNet (rqptr, BeginTable, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); status = SS$_NORMAL; EntryCount = InConfigLevel = 0; FinalRule = false; ThisVirtualService = true; MetaConParseReset (mcptr, true); while (mclptr = MetaConParseRaw (mcptr)) { if (mclptr->Token == METACON_TOKEN_INCLUDE) { vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->IncludeFilePtr; status = FaolToNet (rqptr, RuleIncludeFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); continue; } if (mclptr->Token == METACON_TOKEN_COMMENT) { if (SAME4(mclptr->TextPtr, '!#+#')) { /* from InConfigLoad() add a level */ vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->TextPtr + 5; status = FaolToNet (rqptr, NutherFileFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); InConfigLevel++; } else if (SAME4(mclptr->TextPtr, '!#-#')) { /* from InConfigLoad() remove a level */ if (InConfigLevel) InConfigLevel--; } else { vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->TextPtr; status = FaolToNet (rqptr, CommentFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } continue; } if (mclptr->Token == METACON_TOKEN_SERVICE) { /* if filtering on a virtual service */ if (VirtualService[0] && !StringMatch (rqptr, VirtualService, mclptr->TextPtr)) { ThisVirtualService = false; continue; } ThisVirtualService = true; vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->ConfigProblem || FinalRule; *vecptr++ = mclptr->TextPtr; *vecptr++ = mclptr->ConfigProblem || FinalRule; status = FaolToNet (rqptr, RuleVirtualFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); continue; } else if (mclptr->Number == 1) { vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); status = FaolToNet (rqptr, RuleImplicitVirtualFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } if (!ThisVirtualService) continue; EntryCount++; if (mclptr->Token != METACON_TOKEN_TEXT) { for (cptr = mclptr->TextPtr; *cptr && !ISLWS(*cptr); cptr++); vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); if (mclptr->Token == METACON_TOKEN_VERSION) { *vecptr++ = mclptr->TextPtr; status = FaolToNet (rqptr, VersionFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); continue; } *vecptr++ = mclptr->ConfigProblem || FinalRule || (mclptr->InlineTextPtr && !mclptr->LineDataPtr); *vecptr++ = cptr - mclptr->TextPtr; *vecptr++ = mclptr->TextPtr; while (*cptr && ISLWS(*cptr)) cptr++; *vecptr++ = cptr; *vecptr++ = mclptr->ConfigProblem || FinalRule || (mclptr->InlineTextPtr && !mclptr->LineDataPtr); status = FaolToNet (rqptr, NonRuleFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); continue; } if (!(mrptr = mclptr->LineDataPtr)) { vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->TextPtr; status = FaolToNet (rqptr, ProblemFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); continue; } if (WATCHMOD (rqptr, WATCH_MODULE_DETAIL)) WatchDataFormatted ("!&X !UL !&Z !&Z !&Z\n", mrptr, mrptr->RuleType, mrptr->TemplatePtr, mrptr->ResultPtr, mrptr->ConditionalPtr); vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = mclptr->ConfigProblem || FinalRule; if (mrptr->RuleType == MAPURL_RULE_EXEC) { if (mrptr->IsCgiPlusScript) *vecptr++ = "exec+"; else *vecptr++ = "exec"; } else if (mrptr->RuleType == MAPURL_RULE_FAIL) *vecptr++ = "fail"; else if (mrptr->RuleType == MAPURL_RULE_MAP) *vecptr++ = "map"; else if (mrptr->RuleType == MAPURL_RULE_PASS) *vecptr++ = "pass"; else if (mrptr->RuleType == MAPURL_RULE_PROTECT) *vecptr++ = "protect"; else if (mrptr->RuleType == MAPURL_RULE_REDIRECT) *vecptr++ = "redirect"; else if (mrptr->RuleType == MAPURL_RULE_SCRIPT) { if (mrptr->IsCgiPlusScript) *vecptr++ = "script+"; else *vecptr++ = "script"; } else if (mrptr->RuleType == MAPURL_RULE_SET) *vecptr++ = "set"; else if (mrptr->RuleType == MAPURL_RULE_UXEC) *vecptr++ = "uxec"; else if (mrptr->RuleType == MAPURL_RULE_USER) *vecptr++ = "user"; else *vecptr++ = "*ERROR*"; *vecptr++ = mrptr->TemplatePtr; if (isdigit(*mrptr->ResultPtr)) { *vecptr++ = " "!&;AZ""; *vecptr++ = mrptr->ResultPtr; } else if (mrptr->ResultPtr[0]) { *vecptr++ = " !&;AZ"; *vecptr++ = mrptr->ResultPtr; } else *vecptr++ = ""; if (mrptr->PathSet) { /* just one space, ExplainPathSet() adds it's own leading space */ *vecptr++ = " !&;AZ"; *vecptr++ = MapSetExplain (rqptr, mrptr); } else *vecptr++ = ""; if (mrptr->ConditionalPtr[0]) { *vecptr++ = " !&;AZ"; *vecptr++ = mrptr->ConditionalPtr; } else *vecptr++ = ""; *vecptr++ = mclptr->ConfigProblem || FinalRule; status = FaolToNet (rqptr, RuleFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /* stop reporting a specific virtual service if terminating rule */ if (!VirtualService[0]) continue; if ((mrptr->RuleType == MAPURL_RULE_FAIL || mrptr->RuleType == MAPURL_RULE_PASS) && SAME2(mrptr->TemplatePtr,'*\0')) FinalRule = true; } vecptr = FaoVector; if (EntryCount) *vecptr++ = ""; else *vecptr++ = "(none)\n"; *vecptr++ = MapUrlUserNameCacheEntries; status = FaolToNet (rqptr, UserMappingFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /*****************/ /* user mappings */ /*****************/ EntryCount = 0; /* process the cache entry list from most to least recent */ for (leptr = MapUrlUserNameCacheList.HeadPtr; leptr; leptr = leptr->NextPtr) { ucptr = (MAP_URL_USER_ENTRY*)leptr; /* if empty name, must have been reset, no point in going any further */ if (!ucptr->UserName[0]) break; vecptr = FaoVector; EntryCount++; if (EntryCount % 2) *vecptr++ = " class=\"hlght\""; else *vecptr++ = " class=\"hlght\""; *vecptr++ = EntryCount; *vecptr++ = ucptr->UserName; *vecptr++ = ucptr->UserPath; if (ucptr->PathOds == MAPURL_PATH_ODS_2) *vecptr++ = "ODS-2"; else if (ucptr->PathOds == MAPURL_PATH_ODS_5) *vecptr++ = "ODS-5"; else *vecptr++ = "ods-2"; *vecptr++ = ucptr->HitCount; *vecptr++ = &ucptr->LastTime64; *vecptr++ = ucptr->ReuseCount; status = FaolToNet (rqptr, EntryFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } if (!MapUrlUserNameCacheList.HeadPtr) { status = FaolToNet (rqptr, EmptyUserMappingFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } status = FaolToNet (rqptr, EndPageFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_MAPPING_RULES; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); } /*****************************************************************************/ /* Report an individual rule during processing of a mapping rule check. */ MapUrl_WatchRule ( REQUEST_STRUCT *rqptr, MAP_RULE_META *mrptr, METACON_LINE *mclptr, int InConfigLevel, char *PathPtr, int PathOds, int Match, BOOL SetPathIgnore ) { static char RuleFao [] = "!AZ!AZ!AZ !AZ !8AZ !AZ !&@!&@!&@\n"; BOOL WatchMatch = false; int status; unsigned short Length; unsigned long FaoVector [32]; unsigned long *vecptr; char Buffer [8192], RmsSubChar [2]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_WatchRule() !&Z !UL !UL", PathPtr, PathOds, Match); vecptr = FaoVector; *vecptr++ = MetaConCounter (mclptr, InConfigLevel); *vecptr++ = PathPtr; if (PathOds == MAPURL_PATH_ODS_0) *vecptr++ = " NOODS"; else if (PathOds == MAPURL_PATH_ODS_2) *vecptr++ = " ODS=2"; else if (PathOds == MAPURL_PATH_ODS_5) *vecptr++ = " ODS=5"; else if (PathOds == MAPURL_PATH_ODS_ADS) *vecptr++ = " ODS=ADS"; else if (PathOds == MAPURL_PATH_ODS_PWK) *vecptr++ = " ODS=PWK"; else if (PathOds == MAPURL_PATH_ODS_SMB) *vecptr++ = " ODS=SMB"; else if (PathOds == MAPURL_PATH_ODS_SRI) *vecptr++ = " ODS=SRI"; else *vecptr++ = ""; if (Match == MAPURL_REPORT_MATCH_NOT) *vecptr++ = ".."; else if (Match == MAPURL_REPORT_MATCH_RULE) { *vecptr++ = "YN"; WatchMatch = true; } else if (Match == MAPURL_REPORT_MATCH_RULECOND) { *vecptr++ = "YY"; WatchMatch = true; } else if (Match == MAPURL_REPORT_MATCH_RULENOCOND) { *vecptr++ = "Y-"; WatchMatch = true; } else *vecptr++ = "??"; switch (mrptr->RuleType) { case MAPURL_RULE_EXEC : if (mrptr->IsCgiPlusScript) *vecptr++ = "EXEC+"; else *vecptr++ = "EXEC"; break; case MAPURL_RULE_FAIL : *vecptr++ = "FAIL"; break; case MAPURL_RULE_MAP : *vecptr++ = "MAP"; break; case MAPURL_RULE_PASS : *vecptr++ = "PASS"; break; case MAPURL_RULE_PROTECT : *vecptr++ = "PROTECT"; break; case MAPURL_RULE_REDIRECT : *vecptr++ = "REDIRECT"; break; case MAPURL_RULE_SCRIPT : if (mrptr->IsCgiPlusScript) *vecptr++ = "SCRIPT+"; else *vecptr++ = "SCRIPT"; break; case MAPURL_RULE_SET : if (SetPathIgnore) *vecptr++ = "(SET)"; else *vecptr++ = "SET"; break; case MAPURL_RULE_USER : *vecptr++ = "USER"; break; case MAPURL_RULE_UXEC : if (mrptr->IsCgiPlusScript) *vecptr++ = "UXEC+"; else *vecptr++ = "UXEC"; break; default : *vecptr++ = "*ERROR*"; } *vecptr++ = mrptr->TemplatePtr; if (isdigit(*mrptr->ResultPtr)) { *vecptr++ = "\"!AZ\""; *vecptr++ = mrptr->ResultPtr; } else { *vecptr++ = "!AZ"; *vecptr++ = mrptr->ResultPtr; } if (mrptr->PathSet) { *vecptr++ = " !AZ"; *vecptr++ = MapSetExplain (rqptr, mrptr); } else *vecptr++ = ""; if (mrptr->ConditionalPtr[0]) { *vecptr++ = " !AZ"; *vecptr++ = mrptr->ConditionalPtr; } else *vecptr++ = ""; status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, RuleFao, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); if (WATCHING (rqptr, WATCH_MAPPING)) WatchData (Buffer, Length); } /*****************************************************************************/ /* Supplied with an integer in 'ThrottleIndex' representing the element number of the 'ThrottleArray' path, scan through all the paths in the rule list noting all SET THROTTLE= paths until the count equals that of the integer supplied. This is the rule representing that index. Return a pointer to the rule, or NULL. */ MAP_RULE_META* MapUrl_ThrottleRule (int ThrottleIndex) { int RuleCount; MAP_RULE_META *mrptr; MAP_SET_META *mrpsptr; METACON_LINE *mclptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ThrottleRule() !UL", ThrottleIndex); RuleCount = 0; MetaConParseReset (MetaGlobalMappingPtr, true); while (mclptr = MetaConParseRaw (MetaGlobalMappingPtr)) { if (!(mrptr = mclptr->LineDataPtr)) continue; mrpsptr = &mrptr->mpPathSet; if (!mrpsptr->ThrottleSet) continue; if (ThrottleIndex != RuleCount++) continue; return (mrptr); } return (NULL); } /*****************************************************************************/ /* Reload the mapping rules. */ MapUrl_ControlReload () { /*********/ /* begin */ /*********/ if ((WATCH_MODULE(WATCH_MOD_MAPURL))) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ControlReload()"); MetaConUnload (&MetaGlobalMappingPtr, &MapUrl_ConfigUnloadLineData); MapUrl_ConfigLoad (&MetaGlobalMappingPtr); /* purge DCL module script task list and script name cache */ DclLoadedMappingRules(); } /*****************************************************************************/