/*****************************************************************************/ /* Service.c This module provides a complementary function to CONFIG.C, which basically configures per-HTTPD process runtime characterstics, whereas SERVICE.C allows per-service (essentially virtual services) characteristics to be specified. While there is some overlap between the two they do perform different tasks. Prior to WASD v7.1 service configuration was performed by CONFIG.C as part of general configuration. This had it's limitations and this module goes some way in providing a more flexible and understandable configuration environment. This module configuration file, HTTP$SERVICE, does not have to be used and if is not present the HTTP$CONFIG configuration file [Service] directive will continue to provide backward-compatible service configuration. However, new features will only be incorporated via this module, and HTTP$SERVICE is available it's service configuration overrides anything still present in WASD_CONFIG. All service directives must be delimitted with '[' and ']'. The (virtual) service they apply to are specified with the usual '[[' and ']]' directive. [[http://the.host.name:80]] [ServiceIPaddress] 130.185.250.1 [ServiceProxy] disabled [ServiceNoLog] disabled [ServiceBodyTag] [ServiceErrorReportPath] Both IPv4 and IPv6 addresses may be used to specify a service name and or service address. This can be in normal or compressed form. If part of a service name hyphens should be substituted for colons so that the port can be differentiated from the service address. [[http://130.185.250.1:80]] [[http://--FFFF-130.185.250.1:80]] [[http://--FFFF-1FA-B983:80]] In the case of a service specification a 'generic' host name can be specified using "*". This generic host name is replaced with the IP host name of whichever system starts the server. This is a simple way for providing a basic or common service on all members of a cluster for instance. [[http://*:80]] [ServiceProxy] disabled [ServiceNoLog] disabled This configuration file only really needs to be used for more complex service configurations. Basic HTTP and HTTPS services need only be specified using the the WASD_CONFIG_GLOBAL [Service] directive. The [IncludeFile] directive takes a VMS file name as a parameter. It then attempts to read and insert any directives contained in that file into the current point in the server configuration. Files may be nested one deep (i.e. a main configuration file allowed to include other files, but the other file not allowed in include yet more). PRECEDENCE OF SERVICE SPECIFICATIONS ------------------------------------ 1. /SERVICE= command line qualifier 2. WASD_CONFIG_SERVER configuration file (if logical defined and file exists) 3. WASD_CONFIG_GLOBAL [Service] directive WASD_CONFIG_SERVICE DIRECTIVES ------------------------ o IncludeFile o ServiceAdmin DISABLED | ENABLED o ServiceBind o ServiceBodyTag o ServiceConnect DISABLED | ENABLED o ServiceClientSSL DISABLED | ENABLED o ServiceClientSSLCaFile o ServiceClientSSLcert o ServiceClientSSLcipherList o ServiceClientSSLkey o ServiceClientSSLverifyCA DISABLED | ENABLED o ServiceClientSSLversion o ServiceErrorReportPath o ServiceHttp2Protocol DISABLED | ENABLED (default) o ServiceLogFormat o ServiceNoLog DISABLED | ENABLED o ServiceNonSSLRedirect |[:] o ServiceProxy DISABLED | ENABLED o ServiceProxyAffinity DISABLED | ENABLED o ServiceProxyAuth NONE | PROXY | CHAIN | LOCAL o ServiceProxyCache obsolete as of v12.0.0 o ServiceProxyChain o ServiceProxyChainCred : o ServiceProxyBind o ServiceProxyTunnel CONNECT | FIREWALL | RAW o ServiceProxyReworkMax o ServiceRawSocket DISABLED | ENABLED o ServiceShareSSH o ServiceSSLcert o ServiceSSLcipherList o ServiceSSLcipherSuites o ServiceSSLoptions o ServiceSSLverifyPeer DISABLED | ENABLED | OPTIONAL o ServiceSSLverifyPeerCAfile o ServiceSSLverifyDataMax o ServiceSSLverifyPeerDepth o ServiceSSLversion o ServiceSSLkey o ServiceSSLstrictTransSec [] COMMAND-LINE PARAMETERS ----------------------- The following syntax and values are also available for both the WASD_CONFIG [service] configuration parameter and /SERVICE= qualifier. Also see SESOLA.C for SSL parameter processing. http: system host name, HTTP service https: system host name, SSL service http:port system host name, supplied port, HTTP service https:port system host name, supplied SSL service http://host.domain specified host name, pot 80, HTTP service https://host.domain specified host name port 443, SSL service http://host.domain:port specified host name and port, HTTP service https://host.domain:port specified host name and port, SSL service ;admin service is for per-instance administration ;bind= supplied IP address for service ;ip= supplied IP address for service ;cafile= CA file for verifying X.509 certificates ;cert= SSL service's non-default server certificate ;chain= service chains to up-stream proxy service ;cipher= SSL service's specific, non-default cipher list ;suites= SSL service's specific, non-default cipher list ;connect service provides SSL connect proxy access ;key= SSL service's non-default server RSA private key ;lauth local authorization required ;backlog= listen queue length ;pauth proxy authorization required ;pbind supplied IP address for proxy socket ;pclientssl client SSL supported ;proxy service provide proxy access ;verify always verify SSL connection using X.509 cert For example: [service] http://host.name:8080;proxy;connect https://host.name:443;cert=WASD_LOCAL:SITE.PEM VERSION HISTORY --------------- 08-JUN-2023 MGD [ServiceSSLcipherSuites] for TLSv1.3 14-MAR-2021 MGD proxy caching obsolete 26-FEB-2021 MGD [ServiceConnect] respond at connection on a port [ServiceProxyReworkMax] to allow proxy reworking 15-AUG-2020 MGD if no service configured create http: and https: ex nihilo 30-NOV-2016 MGD [ServiceRawSocket] service is raw [web]socket ensure HTTP/2 is disabled on "tunnel" services 06-JUN-2016 MGD [ServiceSSLsessionTimeout] lifetime of session ticket or ID [ServiceSSLverifyPeerDataMax] bugfix; ServiceReviseNow() next new service (other items) 21-FEB-2015 MGD [ServiceSSLstrictTransSec] 17-JAN-2015 MGD ServiceConfigLoadCallback() StringBuffer[4096]; 15-NOV-2014 MGD [ServiceSSLoptions] 04-OCT-2013 MGD [ServiceNonSSLRedirect] |[:] 06-MAR_2011 MGD ServiceEntityMatch() processes in-match and if-not-match 30-OCT-2010 MGD ServiceFindVirtual() make "Host:" beginning "www." find services with the same remaining domain (eliminates the need to have parallel "www.name.dom" and "name.dom") 12-SEP-2010 MGD ServiceReportNow() add synopsis to service report 10-JUN-2010 MGD bugfix; allow METACON_TOKEN_INCLUDE for [IncludeFile] 27-FEB-2010 MGD [ServiceProxyChainCred] down-stream proxy credentials 21-JAN-2010 MGD ServiceChange() 15-NOV-2009 MGD [ServiceShareSSH] share with (allow proxy to) SSH (timeout) 13-OCT-2009 MGD allow for []-delimited IPv6 address (as service names) ServiceFindVirtual() allow for IPv6 interface in "Host:" 22-AUG-2009 MGD bugfix; ServiceConfigFromString() create and use temporary service structure if generating report 31-JUL-2009 MGD ServiceConfigAdd() use INADDR_ANY if host name lookup fails 26-MAY-2009 MGD [ServiceLogFormat] user defined format 23-OCT-2007 MGD [ServiceProxyAuth] CHAIN 22-APR-2006 MGD [ServiceProxyAffinity] 11-JUN-2005 MGD allow [ServiceErrorReportPath] to specify a negative code to exclude that from error reporting (e.g. -401) 20-APR-2005 MGD add 'SSL Shared With:' report line 20-JAN-2005 MGD bugfix; ServiceConfigAdd(), NetHostNameLookup() status check 14-NOV-2004 MGD bugfix; reporting configuration errors (confusing mention of next service, rather than current service) 15-AUG-2004 MGD [ServiceClientSSLcert], [ServiceClientSSLkey] 10-AUG-2004 MGD 'tunnelling' concept generalises CONNECT method 06-AUG-2004 MGD bugfix; ServiceFindVirtual() port string comparison 10-APR-2004 MGD significant modifications to support IPv6 28-JAN-2004 MGD service access log report (via LoggingReport()) 15-AUG-2003 MGD where CDATA constraints make using entity impossible use a field name of hidden$lf and ^ substituted for it, bugfix; ServiceConfigReviseNow() form element names must be unique (technically correct and enforced by modern browsers) 08-JUN-2003 MGD bugfix; ServiceConfigFromString() (jpp@esme.fr) 28-JAN-2003 MGD relax ServiceParse() so that [[the.host.name]] is accepted (i.e. without both scheme and port) 30-SEP-2002 MGD bugfix; ServiceConfigFromString() 21-SEP-2002 MGD bugfix; make WASD_CONFIG_SERVICE not found non-fatal 15-MAY-2002 MGD change [ServiceProxyHttpSSL] to [ServiceClientSSL] 23-FEB-2002 MGD change [ServiceSSLclientVerify..] to [ServiceSSLverifyPeer..] to avoid confusion with unrelated [ServiceClientSSL..] 06-JAN-2002 MGD [ServiceProxyHttpSSL..] HTTP to SSL (HTTPS) gateway 14-OCT-2001 MGD [ServiceProxyBind], [ServiceIPaddress] to [ServiceBind] 29-SEP-2001 MGD instance support (including admin service) 15-SEP-2001 MGD meta-config 04-AUG-2001 MGD support module WATCHing 04-JUL-2001 MGD additional information in service report 27-JUN-2001 MGD bugfix; parsing of [ServiceProxyChain] 28-FEB-2001 MGD OdsLoadTextFile(), OdsParseTextFile(), [IncludeFile] 16-FEB-2001 MGD add service-based error report status code handling 10-DEC-2000 MGD SSL client certificate verification 21-NOV-2000 MGD allow for generic service (no host name specified) 17-JUN-2000 MGD initial */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include #include /* application related header */ #include "wasd.h" #define WASD_MODULE "SERVICE" /******************/ /* global storage */ /******************/ BOOL ServiceAdminSpecified, ServiceConfigFileDefined; ServiceLoadFromConfigFile, ServiceWwwImplied; SERVICE_META ServiceMeta; SERVICE_META *ServiceMetaPtr; LIST_HEAD ServiceList; char ErrorServiceList [] = "service list confusing"; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL HttpdServerStartup, LoggingPerService, ProtocolHttpsAvailable, ProtocolHttpsConfigured; extern int OpcomMessages, ServerPort; extern char CliServices[], ErrorSanityCheck[], ServerHostName[], ServerHostPort[], SoftwareID[]; extern CONFIG_STRUCT Config; extern META_CONFIG *MetaGlobalServicePtr; extern MSG_STRUCT Msgs; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* */ int ServiceConfigLoad (META_CONFIG **MetaConPtrPtr) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigLoad()"); ServiceWwwImplied = Config.cfMisc.WwwImplied; if (!ServiceConfigFileDefined) ServiceConfigFileDefined = ServiceLoadFromConfigFile = true; if (CliServices[0] || !ServiceLoadFromConfigFile) { /* set services using /SERVICE=, or no service configuration file */ ServiceLoadFromConfigFile = false; status = ServiceConfigFromString (MetaConPtrPtr); } else { ServiceLoadFromConfigFile = true; status = MetaConLoad (MetaConPtrPtr, CONFIG_SERVICE_FILE_NAME, &ServiceConfigLoadCallback, true, false); } if (*MetaConPtrPtr == MetaGlobalServicePtr) { /* server startup */ MetaConStartupReport (MetaGlobalServicePtr, "SERVICE"); if (VMSnok (status)) { /* the WASD_CONFIG_SERVICE file does not *have* to exist! */ if (status != RMS$_FNF) exit (status); status = SS$_NORMAL; } if (!ServiceList.EntryCount) { ErrorNoticed (NULL, 0, "no service configured - ex nihilo...", FI_LI); ServiceLoadFromConfigFile = false; status = ServiceConfigFromString (MetaConPtrPtr); } } return (status); } /*****************************************************************************/ /* Called by MetaConUnload() to free resources allocated during service configuration. */ ServiceConfigUnload (META_CONFIG *mcptr) { int status; SERVICE_META *smptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigUnload()"); smptr = mcptr->ServiceMetaPtr; if (smptr && smptr != ServiceMetaPtr) { LIST_ITERATE (svptr, smptr->ServiceListPtr) { if (svptr->SSLserverPtr) VmFree (svptr->SSLserverPtr, FI_LI); if (svptr->SSLclientPtr) VmFree (svptr->SSLclientPtr, FI_LI); VmFree (svptr, FI_LI); } VmFree (smptr, FI_LI); mcptr->ServiceMetaPtr = NULL; } } /*****************************************************************************/ /* Called after each service has been fully configured. */ ServiceConfigPostProcess (META_CONFIG *mcptr) { static char ProblemCannotConfig [] = "Cannot configure service"; int status; SERVICE_META *smptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigPostProcess()"); smptr = mcptr->ServiceMetaPtr; if (smptr->ServiceLooksValid) { svptr = &smptr->ConfigService; /* disable irrelevant attributes */ if (svptr->RawSocket) { svptr->AdminService = svptr->Http2Enabled = svptr->ProxyService = svptr->ConnectService = false; svptr->ShareSSH = 0; } else if (svptr->ConnectService) { svptr->AdminService = svptr->Http2Enabled = svptr->ProxyService = svptr->RawSocket = false; svptr->ShareSSH = 0; } else if (svptr->ProxyTunnel == PROXY_TUNNEL_CONNECT || svptr->ProxyTunnel == PROXY_TUNNEL_FIREWALL || svptr->ProxyTunnel == PROXY_TUNNEL_RAW) svptr->Http2Enabled = false; mcptr->ReportLinePtr = smptr->ServiceLine; mcptr->ReportLineNumber = smptr->ServiceLineNumber; status = ServiceConfigAdd (mcptr, &smptr->ConfigService); if (VMSnok (status)) MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemCannotConfig); mcptr->ReportLinePtr = NULL; mcptr->ReportLineNumber = 0; } } /*****************************************************************************/ /* 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 ServiceConfigLoadCallback (META_CONFIG *mcptr) { static char ProblemUnknownKeyword [] = "Unknown keyword", ProblemObsolete [] = "Directive is obsolete", ProblemDirective [] = "Unknown directive", ProblemInvalidService [] = "Invalid service specification", ProblemOverflow [] = "Storage overflow", ProblemNoSSL [] = "SSL not available", ProblemUsage [] = "Cannot use during service configuration"; int status, retval, LineLength; char *cptr, *sptr, *zptr; char StringBuffer [4096], StringScratch [4096]; SERVICE_META *smptr; METACON_LINE *mclptr; SERVICE_STRUCT *svptr; SESOLA_CONTEXT *scptr, *ssptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) { WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigLoadCallback() !&F !&X", &ServiceConfigLoadCallback, 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 (mcptr == MetaGlobalServicePtr) { /* at server startup set the global service pointer */ smptr = mcptr->ServiceMetaPtr = ServiceMetaPtr = &ServiceMeta; /* use the global service list */ smptr->ServiceListPtr = &ServiceList; } else if (!mcptr->ServiceMetaPtr) { /* with a report conjure one up ex nihlo */ smptr = mcptr->ServiceMetaPtr = VmGet (sizeof(SERVICE_META)); /* use the internal service list */ smptr->ServiceListPtr = &smptr->ServiceList; } else /* not the first time through */ smptr = mcptr->ServiceMetaPtr; if (mclptr->Token == METACON_TOKEN_PRE) { /******************/ /* pre-initialize */ /******************/ return (true); } if (mclptr->Token == METACON_TOKEN_POST) { /****************/ /* post-process */ /****************/ /* if last loaded service add this to the list */ ServiceConfigPostProcess (mcptr); return (true); } if (mclptr->Token == METACON_TOKEN_INCLUDE) return (true); if (mclptr->Token == METACON_TOKEN_VERSION) return (true); if (mclptr->Token == METACON_TOKEN_COMMENT) if (SAME4 (mclptr->TextPtr, '!#+#') || SAME4 (mclptr->TextPtr, '!#-#') || SAME4 (mclptr->TextPtr, '!#=#')) return (true); if (mclptr->Token != METACON_TOKEN_SERVICE && mclptr->Token != METACON_TOKEN_TEXT) { /* only interested in services and directive text */ MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUsage); return (true); } /***********/ /* process */ /***********/ /* this service config structure will be used as working storage */ svptr = &smptr->ConfigService; /* buffer the text associated with the current "line" */ zptr = (sptr = StringBuffer) + sizeof(StringBuffer); cptr = mclptr->TextPtr; while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemOverflow); return (false); } *sptr = '\0'; cptr = StringBuffer; if (mclptr->Token == METACON_TOKEN_SERVICE) { /***************************/ /* next (or first) service */ /***************************/ /* if previous valid service add this to the list */ ServiceConfigPostProcess (mcptr); /* initialize service structure */ memset (svptr, 0, sizeof(SERVICE_STRUCT)); smptr->ServiceLooksValid = false; /* for MetaconReport() note line details for when ServiceConfigAdd() */ strzcpy (smptr->ServiceLine, mcptr->CurrentOdsPtr->DataLinePtr, sizeof(smptr->ServiceLine)); smptr->ServiceLineNumber = mcptr->CurrentOdsPtr->DataLineNumber; /* enable unless explicitly disabled */ svptr->Http2Enabled = true; /* avoid loading the "next new service" entry ;^) */ if (strchr (cptr, '?')) return (true); /*******************/ /* process service */ /*******************/ smptr->ServiceLooksValid = true; retval = ServiceParse (cptr, &svptr->RequestScheme, &svptr->ServerHostName, sizeof(svptr->ServerHostName), &svptr->ServerPort, &svptr->GenericService); if (retval == -1) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemInvalidService); smptr->ServiceLooksValid = false; } if (svptr->RequestScheme == SCHEME_HTTPS) { if (ProtocolHttpsAvailable) svptr->SSLserverPtr = VmGet (sizeof(SESOLA_CONTEXT)); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemNoSSL); smptr->ServiceLooksValid = false; } } return (true); } /**********************/ /* service directives */ /**********************/ if (*cptr == '[') { cptr++; for (sptr = cptr; *sptr && *sptr != ']'; sptr++); if (*sptr != ']') { MetaConReport (mcptr, METACON_REPORT_ERROR, "missing \']\'?"); return (true); } *sptr++ = '\0'; while (*sptr && ISLWS(*sptr)) sptr++; } /****************************************************/ /* ignore directives unless server specification ok */ /****************************************************/ if (!smptr->ServiceLooksValid) return (true); ssptr = svptr->SSLserverPtr; scptr = svptr->SSLclientPtr; if (strsame (cptr, "ServiceAdmin", -1)) svptr->AdminService = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceBind", -1) || /* and for backward compatibility */ strsame (cptr, "ServiceIPaddress", -1)) { MetaConSetString (mcptr, sptr, svptr->BindIpAddressString, sizeof(svptr->BindIpAddressString)); /* the convention for IPv6 literal hexadecimal is upper-case */ for (cptr = svptr->BindIpAddressString; *cptr; cptr++) *cptr = to_upper(*cptr); } else if (strsame (cptr, "ServiceBodyTag", -1)) MetaConSetString (mcptr, sptr, svptr->BodyTag, sizeof(svptr->BodyTag)); else if (strsame (cptr, "ServiceClientSSL", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); svptr->SSLclientEnabled = MetaConSetBoolean (mcptr, sptr); } else if (strsame (cptr, "ServiceClientSSLCaFile", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->CaFile, sizeof(scptr->CaFile)); } else if (strsame (cptr, "ServiceClientSSLcert", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->CertFile, sizeof(scptr->CertFile)); } else if (strsame (cptr, "ServiceClientSSLcipherList", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->CipherList, sizeof(scptr->CipherList)); } else if (strsame (cptr, "ServiceClientSSLcipherSuites", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->CipherSuites, sizeof(scptr->CipherSuites)); } else if (strsame (cptr, "ServiceClientSSLkey", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->KeyFile, sizeof(scptr->KeyFile)); } else if (strsame (cptr, "ServiceClientSSLverifyCA", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); scptr->VerifyCA = MetaConSetBoolean (mcptr, sptr); } else if (strsame (cptr, "ServiceClientSSLversion", -1)) { if (!scptr) svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, scptr->VersionString, sizeof(scptr->VersionString)); } else if (strsame (cptr, "ServiceErrorReportPath", -1)) MetaConSetString (mcptr, sptr, svptr->ErrorReportPath, sizeof(svptr->ErrorReportPath)); else if (strsame (cptr, "ServiceHttp2Protocol", -1)) svptr->Http2Enabled = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceLogFormat", -1)) MetaConSetString (mcptr, sptr, svptr->LogFormat, sizeof(svptr->LogFormat)); else if (strsame (cptr, "ServiceNoLog", -1)) svptr->NoLog = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceNoTrack", -1)) MetaConReport (mcptr, METACON_REPORT_INFORM, ProblemObsolete); else if (strsame (cptr, "ServiceNonSslRedirect", -1)) MetaConSetString (mcptr, sptr, svptr->NonSslRedirect, sizeof(svptr->NonSslRedirect)); else if (strsame (cptr, "ServiceProxy", -1)) svptr->ProxyService = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceProxyAffinity", -1)) svptr->ProxyAffinity = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceProxyAuth", -1)) { MetaConSetString (mcptr, sptr, StringScratch, sizeof(StringScratch)); if (!StringScratch[0] || strsame (StringScratch, "NONE", -1)) svptr->ProxyAuthRequired = svptr->LocalAuthRequired = svptr->ProxyChainAuthRequired = false; else if (strsame (StringScratch, "PROXY", -1)) svptr->ProxyAuthRequired = true; else if (strsame (StringScratch, "CHAIN", -1)) svptr->ProxyChainAuthRequired = true; else if (strsame (StringScratch, "LOCAL", -1)) svptr->LocalAuthRequired = true; else { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUnknownKeyword); smptr->ServiceLooksValid = false; return (true); } } else if (strsame (cptr, "ServiceProxyBind", -1)) { MetaConSetString (mcptr, sptr, svptr->ProxyBindIpAddressString, sizeof(svptr->ProxyBindIpAddressString)); /* the convention for IPv6 literal hexadecimal is upper-case */ for (cptr = svptr->ProxyBindIpAddressString; *cptr; cptr++) *cptr = to_upper(*cptr); } else if (strsame (cptr, "ServiceProxyCache", -1)) MetaConReport (mcptr, METACON_REPORT_INFORM, ProblemObsolete); else if (strsame (cptr, "ServiceProxyChain", -1)) MetaConSetString (mcptr, sptr, svptr->ProxyChainHostPort, sizeof(svptr->ProxyChainHostPort)); else if (strsame (cptr, "ServiceProxyChainCred", -1)) MetaConSetString (mcptr, sptr, svptr->ProxyChainCred, sizeof(svptr->ProxyChainCred)); else if (strsame (cptr, "ServiceProxyReworkMax", -1)) svptr->ProxyReworkMax = MetaConSetInteger (mcptr, sptr); else if (strsame (cptr, "ServiceProxySSL", -1)) { /* backward compatibility (allows CONNECT) */ if (MetaConSetBoolean (mcptr, sptr)) svptr->ProxyTunnel = PROXY_TUNNEL_CONNECT; else svptr->ProxyTunnel = 0; } else if (strsame (cptr, "ServiceProxyTrack", -1)) MetaConReport (mcptr, METACON_REPORT_INFORM, ProblemObsolete); else if (strsame (cptr, "ServiceProxyTunnel", -1)) { MetaConSetString (mcptr, sptr, StringScratch, sizeof(StringScratch)); if (!StringScratch[0] || strsame (StringScratch, "NONE", -1)) svptr->ProxyTunnel = 0; else if (strsame (StringScratch, "CONNECT", -1)) svptr->ProxyTunnel = PROXY_TUNNEL_CONNECT; else if (strsame (StringScratch, "FIREWALL", -1)) svptr->ProxyTunnel = PROXY_TUNNEL_FIREWALL; else if (strsame (StringScratch, "RAW", -1)) svptr->ProxyTunnel = PROXY_TUNNEL_RAW; else { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUnknownKeyword); smptr->ServiceLooksValid = false; return (true); } } else if (strsame (cptr, "ServiceRawSocket", -1)) svptr->RawSocket = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceConnect", -1)) svptr->ConnectService = MetaConSetBoolean (mcptr, sptr); else if (strsame (cptr, "ServiceShareSSH", -1)) svptr->ShareSSH = MetaConSetInteger (mcptr, sptr); else if (strsame (cptr, "ServiceSSLcert", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->CertFile, sizeof(ssptr->CertFile)); } else if (strsame (cptr, "ServiceSSLcipherList", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->CipherList, sizeof(ssptr->CipherList)); } else if (strsame (cptr, "ServiceSSLcipherSuites", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->CipherSuites, sizeof(ssptr->CipherSuites)); } else if (strsame (cptr, "ServiceSSLkey", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->KeyFile, sizeof(ssptr->KeyFile)); } else if (strsame (cptr, "ServiceSSLsessionLifetime", -1)) svptr->ShareSSH = MetaConSetSeconds (mcptr, sptr, 1); else if (strsame (cptr, "ServiceSSLoptions", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->OptionsString, sizeof(ssptr->OptionsString)); } else if (strsame (cptr, "ServiceSSLverifyPeer", -1) || /* backward compatibility */ strsame (cptr, "ServiceSSLclientVerify", -1) || strsame (cptr, "ServiceSSLclientVerifyRequired", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, StringScratch, sizeof(StringScratch)); if (!StringScratch[0] || strsame (StringScratch, "DISABLED", -1) || strsame (StringScratch, "NO", -1) || strsame (StringScratch, "OFF", -1)) ssptr->VerifyPeer = 0; else if (strsame (StringScratch, "ENABLED", -1) || strsame (StringScratch, "YES", -1) || strsame (StringScratch, "ON", -1)) ssptr->VerifyPeer = SESOLA_VERIFY_PEER_REQUIRED; else if (strsame (StringScratch, "OPTIONAL", -1)) ssptr->VerifyPeer = SESOLA_VERIFY_PEER_OPTIONAL; else { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUnknownKeyword); smptr->ServiceLooksValid = false; return (true); } } else if (strsame (cptr, "ServiceSSLverifyPeerCAfile", -1) || /* backward compatibility */ strsame (cptr, "ServiceSSLclientVerifyCaFile", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->CaFile, sizeof(ssptr->CaFile)); } else if (strsame (cptr, "ServiceSSLverifyPeerDataMax", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); ssptr->VerifyDataMax = MetaConSetInteger (mcptr, sptr); } else if (strsame (cptr, "ServiceSSLverifyPeerDepth", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); ssptr->VerifyDepth = MetaConSetInteger (mcptr, sptr); } else if (strsame (cptr, "ServiceSSLversion", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->VersionString, sizeof(ssptr->VersionString)); } else if (strsame (cptr, "ServiceSSLstrictTransSec", -1)) { if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT)); MetaConSetString (mcptr, sptr, ssptr->StrictTransSec, sizeof(ssptr->StrictTransSec)); } else MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemDirective); return (true); } /*****************************************************************************/ /* Add the service to the loaded list. */ int ServiceConfigAdd ( META_CONFIG *mcptr, SERVICE_STRUCT *svptr ) { static BOOL SetOfficialServer; int status; char *cptr, *sptr, *zptr; SERVICE_META *smptr; SERVICE_STRUCT *nsvptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigAdd() !AZ:!UL", svptr->ServerHostName, svptr->ServerPort); /* get a pointer to the meta-config data */ smptr = mcptr->ServiceMetaPtr; status = NetHostNameLookup (svptr->ServerHostName, svptr->ServerPort, &svptr->ServerHostName, &svptr->ServerHostPort, &svptr->ServerIpAddressString, &svptr->ServerIpAddress, NULL); if (VMSnok (status)) { MetaConReport (mcptr, METACON_REPORT_ERROR, "Problem resolving host name !AZ", svptr->ServerHostName); /* set this to zero so that NetCreateService() uses primary address */ IPADDRESS_ZERO (&svptr->ServerIpAddress); } if (svptr->BindIpAddressString[0]) { status = TcpIpStringToAddress (svptr->BindIpAddressString, &svptr->BindIpAddress); if (VMSnok (status)) { MetaConReport (mcptr, METACON_REPORT_ERROR, "Problem with !AZ bind address !AZ", svptr->ServerHostName, svptr->BindIpAddressString); return (status); } } /************************/ /* check/set parameters */ /************************/ if (svptr->AdminService) { if (ServiceAdminSpecified) { MetaConReport (mcptr, METACON_REPORT_ERROR, "Redundant administration service"); return (STS$K_ERROR); } ServiceAdminSpecified = true; } svptr->ServerHostPortLength = strlen(svptr->ServerHostPort); svptr->ServerHostNameLength = strlen(svptr->ServerHostName); sprintf (svptr->ServerPortString, "%d", svptr->ServerPort); if (svptr->RequestScheme == SCHEME_HTTPS) svptr->RequestSchemeNamePtr = "https:"; else svptr->RequestSchemeNamePtr = "http:"; if (!svptr->ListenBacklog) svptr->ListenBacklog = Config.cfServer.ListenBacklog; if (!svptr->ListenBacklog) svptr->ListenBacklog = DEFAULT_LISTEN_BACKLOG; if (svptr->ErrorReportPath[0]) svptr->ServiceErrorReportPath = true; else strzcpy (svptr->ErrorReportPath, Config.cfReport.ErrorReportPath, sizeof(svptr->ErrorReportPath)); /*****************************/ /* proxy service information */ /*****************************/ if (svptr->ProxyService || svptr->ProxyTunnel) { if (svptr->ProxyChainHostPort[0]) { status = NetHostNameLookup (svptr->ProxyChainHostPort, 0, &svptr->ProxyChainHostName, &svptr->ProxyChainHostPort, &svptr->ProxyChainIpAddressString, &svptr->ProxyChainIpAddress, NULL); if (VMSnok (status)) { MetaConReport (mcptr, METACON_REPORT_ERROR, "Problem resolving host name !AZ", svptr->ProxyChainHostPort); return (status); } svptr->ProxyChainHostNameLength = strlen(svptr->ProxyChainHostName); if (!svptr->ProxyChainPort) svptr->ProxyChainPort = DEFAULT_HTTP_PROXY_PORT; sprintf (svptr->ProxyChainPortString, "%d", svptr->ProxyChainPort); } } if (svptr->ProxyBindIpAddressString[0]) { status = TcpIpStringToAddress (svptr->ProxyBindIpAddressString, &svptr->ProxyBindIpAddress); if (VMSnok (status)) { MetaConReport (mcptr, METACON_REPORT_ERROR, "Problem with !AZ proxy bind address !AZ", svptr->ServerHostName, svptr->ProxyBindIpAddressString); return (status); } } /***************************************/ /* allocate memory add and new service */ /***************************************/ /* copy the filled-out service structure into an in-list structure */ nsvptr = VmGet (sizeof (SERVICE_STRUCT)); memcpy (nsvptr, svptr, sizeof(SERVICE_STRUCT)); if (svptr->SSLserverPtr) { /* SESOLA server structure */ nsvptr->SSLserverPtr = VmGet (sizeof(SESOLA_CONTEXT)); memcpy (nsvptr->SSLserverPtr, svptr->SSLserverPtr, sizeof(SESOLA_CONTEXT)); } if (svptr->SSLclientPtr) { /* SESOLA client structure */ nsvptr->SSLclientPtr = VmGet (sizeof(SESOLA_CONTEXT)); memcpy (nsvptr->SSLclientPtr, svptr->SSLclientPtr, sizeof(SESOLA_CONTEXT)); } ListAddTail (smptr->ServiceListPtr, nsvptr, LIST_ENTRY_TYPE_SERVICE); /*********/ /* other */ /*********/ /* process the error report path and any associated status codes */ for (cptr = nsvptr->ErrorReportPath; *cptr && !ISLWS(*cptr); cptr++); nsvptr->ErrorReportPathLength = cptr - nsvptr->ErrorReportPath; if (*cptr) *cptr++ = '\0'; while (*cptr && ISLWS(*cptr)) cptr++; nsvptr->ErrorReportPathCodesPtr = cptr; nsvptr->ErrorReportPathCodesCount = 0; while (*cptr && nsvptr->ErrorReportPathCodesCount < SERVICE_ERROR_REPORT_PATH_CODES_MAX) { while (*cptr && ISLWS(*cptr)) cptr++; if (isdigit(*cptr) || *cptr == '-') nsvptr->ErrorReportPathCodes[nsvptr->ErrorReportPathCodesCount++] = atoi(cptr); while (*cptr && !ISLWS(*cptr)) cptr++; } if (nsvptr->ErrorReportPathCodesCount >= SERVICE_ERROR_REPORT_PATH_CODES_MAX) MetaConReport (mcptr, METACON_REPORT_ERROR, "Error report path codes problem"); /* process name, etc., generated from the primary port */ if (!SetOfficialServer) { /* the host and port for the "official" server */ SetOfficialServer = ServerPort = nsvptr->ServerPort; sprintf (ServerHostPort, "%s:%d", ServerHostName, ServerPort); } if (nsvptr->RequestScheme == SCHEME_HTTPS) if (ProtocolHttpsAvailable) ProtocolHttpsConfigured = true; return (SS$_NORMAL); } /*****************************************************************************/ /* Parse a HTTP scheme, host name, and integer port number from a string. Defaults apply to missing components. Some basic checking is performed on the components. Return the number of characters scanned from the string, or -1 to indicate a fatal error. */ int ServiceParse ( char *SourceString, int *SchemeValuePtr, char *HostNamePtr, int SizeOfHostName, int *PortNumberPtr, BOOL *GenericServicePtr ) { BOOL IsHost; char *cptr, *sptr, *tptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceParse() !&Z", SourceString); *SchemeValuePtr = *PortNumberPtr = 0; *HostNamePtr = '\0'; *GenericServicePtr = false; cptr = SourceString; if (strsame (cptr, "http:", 5)) { *SchemeValuePtr = SCHEME_HTTP; cptr += 5; } else if (strsame (cptr, "https:", 6)) { *SchemeValuePtr = SCHEME_HTTPS; cptr += 6; } else { /* if it looks like an incorrect scheme specification */ for (tptr = cptr; *tptr && *tptr != '.' && *tptr != ':' && *tptr != '['; tptr++); if (tptr[0] == ':' && !isdigit(tptr[1]) && tptr[1] != '*') return (-1); /* otherwise it defaults to */ *SchemeValuePtr = SCHEME_HTTP; } if (*cptr == '/') cptr++; if (*cptr == '/') cptr++; if (*cptr == '[') { /* IPv6 address */ zptr = (sptr = HostNamePtr) + SizeOfHostName; while (*cptr && *cptr != ']' && sptr < zptr) *sptr++ = *cptr++; if (*cptr == ']' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; } else if (isdigit(*cptr)) { /* check if it's a dotted-decimal address */ for (tptr = cptr; *tptr && isdigit(*tptr); tptr++); if (*tptr == '.') { /* dotted decimal address */ zptr = (sptr = HostNamePtr) + SizeOfHostName; while (*cptr && (*cptr == '.' || isdigit(*cptr)) && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; } } else if (*cptr == '*') { /* generic service */ while (*cptr && *cptr == '*') cptr++; } else { /* alphanumeric host name, or numeric address */ zptr = (sptr = HostNamePtr) + SizeOfHostName; while (*cptr && (*cptr == '*' || *cptr == '.' || isalnum(*cptr) || *cptr == '-' || *cptr == '_') && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) return (-1); *sptr = '\0'; } if (!HostNamePtr[0]) { /* none or generic specified, default to primary server name */ *GenericServicePtr = true; zptr = (sptr = HostNamePtr) + SizeOfHostName; for (tptr = ServerHostName; *tptr && sptr < zptr; *sptr++ = *tptr++); if (sptr >= zptr) return (-1); *sptr = '\0'; } if (isdigit(*cptr) || (cptr[0] == ':' && isdigit(cptr[1]))) { /* IP port */ if (*cptr == ':') cptr++; *PortNumberPtr = atol(cptr); while (*cptr && isdigit(*cptr)) cptr++; if (*PortNumberPtr <= 0 || *PortNumberPtr > 65535) return (-1); } else if (*SchemeValuePtr == SCHEME_HTTPS) *PortNumberPtr = DEFAULT_HTTPS_PORT; else *PortNumberPtr = DEFAULT_HTTP_PORT; return (cptr - SourceString); } /*****************************************************************************/ /* Parse the 'Service's parameter to determine which host(s)/port(s) services need to be provided for. This is a comma or newline separated list with no white-space. The service information may comprise of optional components: "[scheme://][host][:port][;bind=address][;proxy][;chain=server][;cert=file]". */ int ServiceConfigFromString (META_CONFIG **MetaConPtrPtr) { BOOL ProxyService, SesolaServiceUsed; int status, DummyRequestScheme, Length, ListenBacklog; char *cptr, *sptr, *zptr; META_CONFIG *mcptr; SERVICE_META *smptr; SERVICE_STRUCT *svptr; SESOLA_CONTEXT SesolaServerService; SERVICE_STRUCT ConfigService; char StringScratch [256]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceConfigFromString() !&Z !&Z !AZ:!UL", CliServices, Config.cfServer.ServicePtr, ServerHostName, ServerPort); status = MetaConLoad (MetaConPtrPtr, NULL, NULL, false, false); if (VMSnok (status)) return (status); mcptr = *MetaConPtrPtr; if (mcptr == MetaGlobalServicePtr) { /* during server startup set the global service pointer */ smptr = mcptr->ServiceMetaPtr = ServiceMetaPtr = &ServiceMeta; /* use the global service list */ smptr->ServiceListPtr = &ServiceList; } else { /* a report then conjure one up ex nihlo */ smptr = mcptr->ServiceMetaPtr = VmGet (sizeof(SERVICE_META)); /* use the internal service list */ smptr->ServiceListPtr = &smptr->ServiceList; } if (CliServices[0]) cptr = CliServices; else if (!Config.cfServer.ServicePtr) { if (ProtocolHttpsAvailable) sprintf (cptr = CliServices, "http://%s:80,https://%s:443", ServerHostName, ServerHostName); else sprintf (cptr = CliServices, "http://%s:80", ServerHostName); } else cptr = Config.cfServer.ServicePtr; /******************************/ /* parse comma-separated list */ /******************************/ while (*cptr) { while (*cptr && (isspace(*cptr) || *cptr == ',')) cptr++; if (!*cptr) break; memset (&ConfigService, 0, sizeof(ConfigService)); memset (&SesolaServerService, 0, sizeof(SesolaServerService)); SesolaServiceUsed = false; /*************************/ /* service specification */ /*************************/ Length = ServiceParse (cptr, &ConfigService.RequestScheme, &ConfigService.ServerHostName, sizeof(ConfigService.ServerHostName), &ConfigService.ServerPort, &ConfigService.GenericService); if (Length < 0) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); cptr += Length; if (!ProtocolHttpsAvailable && ConfigService.RequestScheme == SCHEME_HTTPS) { FaoToStdout ("%HTTPD-W-SERVICE, \ SSL not available, service ignored\n \\!AZ//!AZ:!UL\\\n", ConfigService.RequestSchemeNamePtr, ConfigService.ServerHostPort); continue; } if (ConfigService.RequestScheme == SCHEME_HTTPS) { ConfigService.RequestSchemeNamePtr = "https:"; SesolaServiceUsed = true; } else ConfigService.RequestSchemeNamePtr = "http:"; /****************************/ /* other service parameters */ /****************************/ while (*cptr == ';') { if (strsame (cptr, ";admin", 6)) ConfigService.AdminService = true; else if (strsame (cptr, ";backlog=", 9)) { cptr += 9; ConfigService.ListenBacklog = atoi(cptr); } else if (strsame (cptr, ";bind=", 6) || /* for backward compatibility */ strsame (cptr, ";ip=", 4)) { while (*cptr && *cptr != '=') cptr++; if (*cptr) cptr++; zptr = (sptr = ConfigService.BindIpAddressString) + sizeof(ConfigService.BindIpAddressString); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; } else if (strsame (cptr, ";cafile=", 8)) { cptr += 8; zptr = (sptr = SesolaServerService.CaFile) + sizeof(SesolaServerService.CaFile); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = to_upper(*cptr++); if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; SesolaServiceUsed = true; } else if (strsame (cptr, ";cert=", 6)) { cptr += 6; zptr = (sptr = SesolaServerService.CertFile) + sizeof(SesolaServerService.CertFile); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = to_upper(*cptr++); if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; SesolaServiceUsed = true; } else if (strsame (cptr, ";chain=", 7)) { cptr += 7; zptr = (sptr = ConfigService.ProxyChainHostPort) + sizeof(ConfigService.ProxyChainHostPort); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; } else if (strsame (cptr, ";connect", 8)) ConfigService.ProxyTunnel == PROXY_TUNNEL_CONNECT; else if (strsame (cptr, ";cipher=", 8)) { cptr += 8; zptr = (sptr = SesolaServerService.CipherList) + sizeof(SesolaServerService.CipherList); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; SesolaServiceUsed = true; } else if (strsame (cptr, ";suites=", 8)) { cptr += 8; zptr = (sptr = SesolaServerService.CipherSuites) + sizeof(SesolaServerService.CipherSuites); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; SesolaServiceUsed = true; } else if (strsame (cptr, ";key=", 5)) { cptr += 5; zptr = (sptr = SesolaServerService.KeyFile) + sizeof(SesolaServerService.KeyFile); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = to_upper(*cptr++); if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; SesolaServiceUsed = true; } else if (strsame (cptr, ";lauth", 6)) ConfigService.LocalAuthRequired = true; else if (strsame (cptr, ";paff", 6)) ConfigService.ProxyAffinity = true; else if (strsame (cptr, ";pauth", 6)) ConfigService.ProxyAuthRequired = true; else if (strsame (cptr, ";cauth", 6)) ConfigService.ProxyChainAuthRequired = true; else if (strsame (cptr, ";proxy", 6)) ConfigService.ProxyService = true; else if (strsame (cptr, ";pbind=", 7)) { cptr += 7; zptr = (sptr = ConfigService.ProxyBindIpAddressString) + sizeof(ConfigService.ProxyBindIpAddressString); while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); *sptr = '\0'; } else if (strsame (cptr, ";pclientssl", 11)) { if (!ConfigService.SSLclientPtr) ConfigService.SSLclientPtr = VmGet (sizeof(SESOLA_CONTEXT)); ConfigService.SSLclientEnabled = true; } else if (strsame (cptr, ";verify", 7)) SesolaServiceUsed = SesolaServerService.VerifyPeer = true; else { if (*cptr == ';') cptr++; sptr = cptr; while (*sptr && *sptr != ';' && *sptr != ',') sptr++; FaoToStdout ("%HTTPD-W-SERVICE, \ unknown parameter, ignored\n \\!#AZ\\\n", sptr-cptr, cptr); cptr = sptr; } if (*cptr == ';') cptr++; while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';') cptr++; } /***************/ /* add service */ /***************/ if (SesolaServiceUsed) ConfigService.SSLserverPtr = &SesolaServerService; status = ServiceConfigAdd (mcptr, &ConfigService); if (VMSnok (status)) { FaoToStdout ("%HTTPD-W-SERVICE, \ cannot configure service\n \\!AZ//!AZ\\\n", ConfigService.RequestSchemeNamePtr, ConfigService.ServerHostPort); continue; } if (*cptr && *cptr != ',' && !isspace(*cptr)) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI); } return (SS$_NORMAL); } /*****************************************************************************/ /* Search the list of virtual services for one matching. If either host or port component is omitted or specified as a wildcard then match any. */ BOOL ServiceIsConfigured (char *HostPort) { char *cptr, *sptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceIsConfigured() !&Z", HostPort); LIST_ITERATE (svptr, &ServiceList) { if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchDataFormatted ("!&Z\n", svptr->ServerHostPort); cptr = HostPort; sptr = svptr->ServerHostPort; /* compare the host name portion */ if (*cptr == ':') { if (*sptr == '[') { /* IPv6 address */ while (*sptr && *sptr != ']') sptr++; if (*sptr == ']') sptr++; } else while (*sptr && *sptr != ':') sptr++; } while (*cptr && *cptr != ':' && *sptr && *sptr != ':') { if (*cptr == '*') { while (*cptr == '*') cptr++; while (*sptr && to_lower(*sptr) != to_lower(*cptr)) sptr++; if (!*cptr || *cptr == ':' || !*sptr || *sptr == ':') break; } if (to_lower(*cptr) != to_lower(*sptr)) break; cptr++; sptr++; } /* continue if the host name portion did not match */ if ((*cptr && *cptr != ':') || *sptr != ':') continue; /* return true if the parameter did not contain a specific port */ if (!*cptr) { if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "YES"); return (true); } cptr++; sptr++; /* compare the port portion */ while (*cptr && *sptr) { if (*cptr == '*') { while (*cptr == '*') cptr++; while (*sptr && *sptr != *cptr) sptr++; if (!*cptr || !*sptr) break; } if (*cptr != *sptr) break; cptr++; sptr++; } /* return true if the ports matched */ if (!*cptr && !*sptr) { if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "YES"); return (true); } } if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "NO"); return (false); } /*****************************************************************************/ /* Find a service' host name and port that matches the request's "Host:" request header line. Point the request's service pointer at the virtual service's structure is found. Return true if a matching service was found, false if not. */ BOOL ServiceFindVirtual (REQUEST_STRUCT *rqptr) { BOOL wwwName; int HostNameLength, HostPortLength; char *cptr, *sptr, *sptr1, *sptr2, *zptr; char Ipv6HostSansIf [256]; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceFindVirtual() !&Z", rqptr->rqHeader.HostPtr); if (rqptr->NetIoPtr->SesolaPtr) if (SesolaSNIserviceSet (rqptr->NetIoPtr->SesolaPtr)) return (true); if (!rqptr->rqHeader.HostPtr || !rqptr->rqHeader.HostPtr[0]) { /* just use whatever service the request connected to */ if (WATCHING (rqptr, WATCH_CONNECT)) WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "ACTUAL !AZ", rqptr->ServicePtr->ServerHostPort); return (true); } cptr = rqptr->rqHeader.HostPtr; if (*cptr == '[') { /* IPv6 (one form of anyway) */ for (sptr1 = cptr; *sptr1 && *sptr1 != ']' && *sptr1 != '%'; sptr1++); if (*sptr1 == '%') { /* copy, excising the interface */ zptr = (sptr = Ipv6HostSansIf) + sizeof(Ipv6HostSansIf)-1; while (*cptr && *cptr != '%' && sptr < zptr) *sptr++ = *cptr++; while (*cptr && *cptr != ']') cptr++; sptr1 = sptr; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; cptr = Ipv6HostSansIf; } if (*sptr1 == ']') sptr1++; } else { /* IPv4 */ for (sptr1 = cptr; *sptr1 && *sptr1 != ':'; sptr1++); } HostNameLength = sptr1 - cptr; while (*sptr1) sptr1++; HostPortLength = sptr1 - cptr; if (ServiceWwwImplied) wwwName = SAME4(cptr,'www.'); else wwwName = false; LIST_ITERATE (svptr, &ServiceList) { if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "!UL !UL !&Z !&B !UL !UL !&Z", HostNameLength, HostPortLength, cptr, wwwName, svptr->ServerHostNameLength, svptr->ServerHostPortLength, svptr->ServerHostPort); if (!wwwName || HostNameLength != svptr->ServerHostNameLength + 4) if (HostNameLength != svptr->ServerHostNameLength) continue; if (HostNameLength == HostPortLength) { /* no port specified, use default for request scheme */ switch (rqptr->ServicePtr->RequestScheme) { case SCHEME_HTTP : if (svptr->ServerPort != DEFAULT_HTTP_PORT) continue; break; case SCHEME_HTTPS : if (svptr->ServerPort != DEFAULT_HTTPS_PORT) continue; break; default : continue; } } else if (HostPortLength != svptr->ServerHostPortLength) continue; sptr1 = cptr; /* only if a port was included in the "Host:" compare that */ if (HostNameLength == HostPortLength) sptr2 = svptr->ServerHostName; else sptr2 = svptr->ServerHostPort; /* if "Host:" name begins "www." and this one does not */ if (wwwName && !MATCH4 (sptr2, "www.")) sptr1 += 4; while (*sptr1 && *sptr2) { if (to_lower(*sptr1) == to_lower(*sptr2)) { sptr1++; sptr2++; } else break; } if (*sptr1 || *sptr2) continue; if (WATCHING (rqptr, WATCH_CONNECT)) WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "VIRTUAL !AZ", svptr->ServerHostPort); /* change the request's service pointer to it's virtual equivalent */ rqptr->ServicePtr = rqptr->NetIoPtr->ServicePtr = svptr; return (true); } if (WATCHING (rqptr, WATCH_CONNECT)) WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "UNKNOWN service"); return (false); } /*****************************************************************************/ /* Look for a matching service and change the request to point to that service. If none matching found then a 500 (internal - server configuration) error. Of course just changing the request pointer to another service can have profound implications - consider changing an HTTP request to an SSL service or vice versa! */ BOOL ServiceChange ( REQUEST_STRUCT *rqptr, char *ServiceSpec ) { SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceChange() !&Z", ServiceSpec); if (strsame (ServiceSpec, "RAW", -1)) { /* find the (first) service offering raw tunneling */ LIST_ITERATE (svptr, &ServiceList) if (svptr->ProxyService && svptr->ProxyTunnel == PROXY_TUNNEL_RAW) break; } else if (strsame (ServiceSpec, "http://", 7)) { /* find the matching HTTP service */ LIST_ITERATE (svptr, &ServiceList) { if (svptr->RequestScheme != SCHEME_HTTP) continue; if (StringMatch (rqptr, svptr->ServerHostPort, ServiceSpec+7)) break; } } else if (strsame (ServiceSpec, "https://", 8)) { /* find the matching SSL service */ LIST_ITERATE (svptr, &ServiceList) { if (svptr->RequestScheme != SCHEME_HTTPS) continue; if (StringMatch (rqptr, svptr->ServerHostPort, ServiceSpec+8)) break; } } else { /* find the matching service */ LIST_ITERATE (svptr, &ServiceList) if (StringMatch (rqptr, svptr->ServerHostPort, ServiceSpec)) break; } if (svptr) { if (WATCHING (rqptr, WATCH_REQUEST)) WatchThis (WATCHITM(rqptr), WATCH_REQUEST, "SERVICE change !AZ MATCHED !AZ!AZ -> !AZ!AZ", ServiceSpec, rqptr->ServicePtr->RequestSchemeNamePtr, rqptr->ServicePtr->ServerHostPort, svptr->RequestSchemeNamePtr, svptr->ServerHostPort); /* move the request to that service */ rqptr->ServiceBeforeChangePtr = rqptr->ServicePtr; rqptr->ServicePtr = rqptr->NetIoPtr->ServicePtr = svptr; return (true); } if (WATCHING (rqptr, WATCH_REQUEST)) WatchThis (WATCHITM(rqptr), WATCH_REQUEST, "SERVICE change !AZ NOMATCH", ServiceSpec); rqptr->rqResponse.HttpStatus = 500; return (false); } /*****************************************************************************/ /* Output string configured services (called from ConfigReportNow()). */ int ServiceReportConfigFromString ( REQUEST_STRUCT *rqptr, CONFIG_STRUCT *cfptr ) { static char ServicesFao [] = "

\n\ \n\ \n\
Service
\n\ \n"; static char ServiceLoadFao [] = "\n"; static char ServiceNoneFao [] = "\n"; static char OneServiceFao [] = "\ \ \n"; static char ServiceListFao [] = "\n"; static char EndServicesFao [] = "\n\
(see "Services" report)
(none)
!UL.!AZ//!AZ!AZ!&@!AZ!&@!&@!AZ!AZ!AZ!AZ!AZ!AZ
!UL.!AZ
Service Not Found URL: !AZ
\n\
\n"; static char NotNoneFao [] = "(none)\n"; int count, status; unsigned long FaoVector [32]; char *cptr, *sptr; unsigned long *vecptr; SERVICE_STRUCT *svptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SERVICE)) WatchThis (WATCHALL, WATCH_MOD_SERVICE, "ServiceReportConfigFromString()"); status = FaolToNet (rqptr, ServicesFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (ServiceLoadFromConfigFile) { status = FaolToNet (rqptr, ServiceLoadFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } else if (!cfptr->cfServer.ServicePtr) { status = FaolToNet (rqptr, ServiceNoneFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } else if (cfptr != &Config) { count = 1; for (cptr = cfptr->cfServer.ServicePtr; *cptr; cptr++) { sptr = cptr; vecptr = FaoVector; *vecptr++ = count++; *vecptr++ = sptr; while (*cptr && *cptr != ',') cptr++; if (*cptr) { *cptr = '\0'; status = FaolToNet (rqptr, ServiceListFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); *cptr = ','; } else { status = FaolToNet (rqptr, ServiceListFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } } else { count = 1; LIST_ITERATE (svptr, &ServiceList) { vecptr = FaoVector; *vecptr++ = count++; *vecptr++ = svptr->RequestSchemeNamePtr; *vecptr++ = svptr->ServerHostPort; if (svptr->ProxyChainHostPort[0]) { *vecptr++ = ";chain=!AZ"; *vecptr++ = svptr->ProxyChainHostPort; } else *vecptr++ = ""; if (svptr->ProxyTunnel == PROXY_TUNNEL_CONNECT) *vecptr++ = ";connect"; else *vecptr++ = ""; if (svptr->BindIpAddressString[0]) { *vecptr++ = ";ip=!AZ"; *vecptr++ = svptr->BindIpAddressString; } else *vecptr++ = ""; if (svptr->ListenBacklog != DEFAULT_LISTEN_BACKLOG) { *vecptr++ = ";backlog=!UL"; *vecptr++ = svptr->ListenBacklog; } else *vecptr++ = ""; if (svptr->LocalAuthRequired) *vecptr++ = ";lauth"; else *vecptr++ = ""; if (svptr->ProxyAffinity) *vecptr++ = ";paff"; else *vecptr++ = ""; if (svptr->ProxyAuthRequired) *vecptr++ = ";pauth"; else *vecptr++ = ""; if (svptr->ProxyChainAuthRequired) *vecptr++ = ";cauth"; else *vecptr++ = ""; if (svptr->ProxyService) *vecptr++ = ";proxy"; else *vecptr++ = ""; *vecptr++ = svptr->ServerIpAddressString; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, OneServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } vecptr = FaoVector; if (cfptr->cfServer.ServiceNotFoundUrl[0]) *vecptr++ = cfptr->cfServer.ServiceNotFoundUrl; else *vecptr++ = NotNoneFao; status = FaolToNet (rqptr, EndServicesFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); return (SS$_NORMAL); } /*****************************************************************************/ /* A server administration report on the server's configuration. This function just wraps the reporting function, loading a temporary database if necessary for reporting from the configuration file. */ ServiceReport ( REQUEST_STRUCT *rqptr, BOOL UseServerDatabase ) { int status; META_CONFIG *mcptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceReport() !&F !UL", &ServiceReport, UseServerDatabase); if (UseServerDatabase) ServiceReportNow (rqptr, MetaGlobalServicePtr); else { status = ServiceConfigLoad (&mcptr); if (VMSnok (status)) { /* severe error reported */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI); } else ServiceReportNow (rqptr, mcptr); MetaConUnload (&mcptr, NULL); } AdminEnd (rqptr); } /*****************************************************************************/ /* Return a report on the HTTPd service configuration ... now! This function blocks while executing. */ ServiceReportNow ( REQUEST_STRUCT *rqptr, META_CONFIG *mcptr ) { #define REPORT_ENDIS(b) \ { if (b) *vecptr++ = "[enabled]"; else *vecptr++ = "[disabled]"; } #define REPORT_STRING_DEFAULT(b) \ { if (b && b[0]) *vecptr++ = b; else *vecptr++ = ReportDefault; } #define REPORT_STRING_NONE(b) \ { if (b && b[0]) *vecptr++ = b; else *vecptr++ = ReportNotNone; } static char ReportSynopsisFao [] = "

\n\ \n\ \n\
Synopsis
\n\ \n\ \ \ \ \ \ \ \ \ \n"; static char ReportOneSynopsisFao [] = "\ \ \ \ \ \ \ \ \ \n"; static char ReportEndSynopsisFao [] = "
ServiceAddressIPHTTPSSLProxyTunnelChain
!UL.!AZ//!AZ!AZ!&?v4\rv6\r!AZ!AZ!AZ!AZ!AZ!&@!AZ
\n\
\n"; static char ReportServiceFao [] = "

\n\ \n\ \n\
  !UL  -  \ !AZ//!AZ
\n\ \n\ \n\
\n\ \n\ \n\ \n\ \n\ \n\ !&@\ \n"; static char ReportHttp2Fao [] = "\n\ \n"; static char ReportSslServiceFao [] = "\n\ \n\ \ \n\ \ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \ \n\ \ \n"; static char ReportProxyFao [] = "\n\ \n"; static char ReportProxyDetailsFao [] = "\n\ \n\ \n\ \n\ \n\ \n\ \n"; static char ReportOtherFao [] = "\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\
Service IP Address:!AZ
Bind IP Address:!AZ
MultiHome IP Address:!&I
Socket IP Address:!&@
Device:!&@
HTTP/2:!&@
SSL Version:!AZ
SSL Options:!AZ
SSL Cipher List:!AZ
SSL Cipher Suites:!AZ
SSL Certificate:!AZ
SSL Private Key:!AZ
SSL Session Lifetime:!AZ!AZ
SSL Verify Peer:!AZ
SSL Verify Peer Data Max:!UL!AZ
SSL Verify Peer CA File:!AZ
Strict Transport Security:!AZ
SSL Shared With:"; static char ReportSslClientFao [] = "
SSL/Client!AZ
SSL/Client Version:!AZ
SSL/Client Certificate:!AZ
SSL/Client Private Key:!AZ
SSL/Client Verify CA:!AZ
SSL/Client Verify CA File:!AZ
SSL/Client Cipher List:!AZ
SSL/Client Cipher Suites:!AZ
Proxy:!AZ
Proxy Tunnel:!AZ
Proxy Bind IP Address:!AZ
Proxy Affinity:!AZ
Proxy Authorization:!AZ
Proxy Chain:!AZ
Proxy Chain Cred:!AZ
Proxy Rework Max:!UL
RawSocket:!AZ
(On) Connect:!AZ
Share SSH:!UL\  (!&?enabled\rdisabled\r)
Admin:!&@
NoLog:!AZ
Log Format:!AZ!AZ
Body Tag:!AZ
Report Path:!&@
Access Log:!&@
\n\
\n\
\n"; static char EndPageFao [] = "\n\ \n\ \n"; static char ReportDefault [] = "(default)", ReportNotAppl [] = "(n/a)", ReportNotNone [] = "(none)", ReportServiceNoneFao [] = "(no services defined)"; int count, idx, status, SslShareCount; unsigned long *vecptr; unsigned long FaoVector [64]; char *cptr, *sptr, *zptr; LIST_HEAD *slptr; SERVICE_META *smptr; SERVICE_STRUCT *svptr, *tsvptr; SESOLA_CONTEXT *scptr, *ssptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceReportNow()"); /* get a pointer to the meta-config data */ smptr = mcptr->ServiceMetaPtr; AdminPageTitle (rqptr, "Service Configuration"); AdminMetaConReport (rqptr, mcptr, MetaGlobalServicePtr); if (ServiceConfigFileDefined) AdminMetaConSource (rqptr, mcptr, MetaGlobalServicePtr); /************/ /* synopsis */ /************/ status = FaolToNet (rqptr, ReportSynopsisFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); count = 0; LIST_ITERATE (svptr, smptr->ServiceListPtr) { count++; vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->RequestSchemeNamePtr; *vecptr++ = svptr->ServerHostPort; if (svptr->BindIpAddressString[0]) *vecptr++ = svptr->BindIpAddressString; else *vecptr++ = svptr->ServerIpAddressString; *vecptr++ = IPADDRESS_IS_V4(&svptr->ServerIpAddress); *vecptr++ = svptr->Http2Enabled ? "2+1" : "1"; if (svptr->SSLserverPtr) *vecptr++ = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->VersionStringPtr; else *vecptr++ = ""; if (svptr->ProxyService) *vecptr++ = "enabled"; else *vecptr++ = ""; if (svptr->ShareSSH) *vecptr++ = "ShareSSH "; else *vecptr++ = ""; if (!svptr->ProxyService) *vecptr++ = ""; else if (svptr->ProxyTunnel == PROXY_TUNNEL_CONNECT) *vecptr++ = "CONNECT"; else if (svptr->ProxyTunnel == PROXY_TUNNEL_FIREWALL) *vecptr++ = "FIREWALL"; else if (svptr->ProxyTunnel == PROXY_TUNNEL_RAW) *vecptr++ = "RAW"; else *vecptr++ = ""; if (!svptr->ProxyService) *vecptr++ = ""; else if (svptr->SSLclientPtr && ((SESOLA_CONTEXT*)svptr->SSLclientPtr)->VersionStringPtr) { *vecptr++ = "+!AZ"; *vecptr++ = ((SESOLA_CONTEXT*)svptr->SSLclientPtr)->VersionStringPtr; } else *vecptr++ = ""; if (svptr->ProxyChainHostPort[0]) *vecptr++ = svptr->ProxyChainHostPort; else *vecptr++ = ""; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportOneSynopsisFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } status = FaolToNet (rqptr, ReportEndSynopsisFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /************/ /* services */ /************/ count = 0; LIST_ITERATE (svptr, smptr->ServiceListPtr) { count++; vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->RequestSchemeNamePtr; *vecptr++ = svptr->ServerHostPort; *vecptr++ = svptr->RequestSchemeNamePtr; *vecptr++ = svptr->ServerHostPort; *vecptr++ = svptr->ServerIpAddressString; REPORT_STRING_NONE (svptr->BindIpAddressString) *vecptr++ = &svptr->MultiHomeIpAddress; if (svptr->ServerChannel) { *vecptr++ = "!&I!&? (INADDR_ANY)\r\r"; *vecptr++ = &svptr->ServerIpAddress; *vecptr++ = IPADDRESS_IS_ANY (&svptr->ServerIpAddress); } else if (mcptr == MetaGlobalServicePtr) { if (svptr->BgDevName[0]) { *vecptr++ = "(!AZ)"; *vecptr++ = svptr->BgDevName; } else *vecptr++ = ReportNotNone; } else *vecptr++ = ReportNotAppl; if (mcptr == MetaGlobalServicePtr) { if (VMSnok (svptr->ServerBindStatus)) { if (svptr->ServerBindStatus) { *vecptr++ = "Bind:\ !&m\n"; *vecptr++ = svptr->ServerBindStatus; } else *vecptr++ = "Bind:reused existing socket\n"; } else *vecptr++ = ""; } else *vecptr++ = ""; if (svptr->ServerChannel) { *vecptr++ = "!AZ (!UL channel!%s)"; *vecptr++ = svptr->BgDevName; *vecptr++ = NetGetRefCnt (svptr->ServerChannel); } else if (svptr->BgDevName[0]) { *vecptr++ = "(!AZ)"; *vecptr++ = svptr->BgDevName; } else *vecptr++ = ReportNotNone; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = FaoVector; REPORT_ENDIS (svptr->Http2Enabled) status = FaolToNet (rqptr, ReportHttp2Fao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (ssptr = svptr->SSLserverPtr) { vecptr = FaoVector; REPORT_STRING_DEFAULT (ssptr->VersionString) REPORT_STRING_DEFAULT (ssptr->OptionsString) REPORT_STRING_DEFAULT (ssptr->CipherList) REPORT_STRING_DEFAULT (ssptr->CipherSuites) REPORT_STRING_DEFAULT (ssptr->CertFile) REPORT_STRING_DEFAULT (ssptr->KeyFile) *vecptr++ = MetaConShowSeconds (rqptr, ssptr->SessionLifetime); *vecptr++ = ssptr->SessionLifetime ? "" : " (default)"; switch (ssptr->VerifyPeer) { case SESOLA_VERIFY_PEER_OPTIONAL : *vecptr++ = "[optional]"; break; case SESOLA_VERIFY_PEER_REQUIRED : *vecptr++ = "[enabled]"; break; default : *vecptr++ = "[disabled]"; } *vecptr++ = ssptr->VerifyDataMax; *vecptr++ = ssptr->VerifyDataMax ? "" : " (default)"; REPORT_STRING_DEFAULT (ssptr->CaFile) REPORT_STRING_DEFAULT (ssptr->StrictTransSec) FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportSslServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); SslShareCount = 0; LIST_ITERATE (tsvptr, smptr->ServiceListPtr) { if (tsvptr == svptr) continue; if (tsvptr->RequestScheme != SCHEME_HTTPS) continue; if (!IPADDRESS_IS_SAME (&tsvptr->MultiHomeIpAddress, &svptr->MultiHomeIpAddress)) continue; if (tsvptr->ServerPort != svptr->ServerPort) continue; SslShareCount++; FaoToNet (rqptr, "!AZ
", tsvptr->ServerHostPort); } if (SslShareCount) FaoToNet (rqptr, "\n"); else FaoToNet (rqptr, "!AZ\n", ReportNotNone); } if (ssptr && svptr->NonSslRedirect[0]) FaoToNet (rqptr, "Non-SSL Redirect:\ !&;AZ\n", svptr->NonSslRedirect); if (scptr = svptr->SSLclientPtr) { vecptr = FaoVector; REPORT_ENDIS (svptr->SSLclientEnabled) REPORT_STRING_DEFAULT (scptr->VersionStringPtr) REPORT_STRING_DEFAULT (scptr->CertFilePtr) REPORT_STRING_DEFAULT (scptr->KeyFilePtr) REPORT_ENDIS (scptr->VerifyCA) REPORT_STRING_DEFAULT (scptr->CaFilePtr) REPORT_STRING_DEFAULT (scptr->CipherListPtr) REPORT_STRING_DEFAULT (scptr->CipherSuitesPtr) FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportSslClientFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; REPORT_ENDIS (svptr->ProxyService) status = FaolToNet (rqptr, ReportProxyFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (svptr->ProxyService) { vecptr = FaoVector; if (svptr->ProxyTunnel == PROXY_TUNNEL_CONNECT) *vecptr++ = "CONNECT"; else if (svptr->ProxyTunnel == PROXY_TUNNEL_FIREWALL) *vecptr++ = "FIREWALL"; else if (svptr->ProxyTunnel == PROXY_TUNNEL_RAW) *vecptr++ = "RAW"; else *vecptr++ = ReportNotNone; REPORT_STRING_NONE (svptr->ProxyBindIpAddressString) REPORT_ENDIS (svptr->ProxyAffinity) if (svptr->ProxyAuthRequired) *vecptr++ = "PROXY"; else if (svptr->ProxyChainAuthRequired) *vecptr++ = "CHAIN"; else if (svptr->LocalAuthRequired) *vecptr++ = "LOCAL"; else *vecptr++ = ReportNotNone; REPORT_STRING_NONE (svptr->ProxyChainHostPort) REPORT_STRING_NONE (svptr->ProxyChainCred) *vecptr++ = svptr->ProxyReworkMax; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportProxyDetailsFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; REPORT_ENDIS (svptr->RawSocket) REPORT_ENDIS (svptr->ConnectService) *vecptr++ = svptr->ShareSSH; *vecptr++ = svptr->ShareSSH; if (svptr->AdminService) *vecptr++ = "!AZ"; REPORT_ENDIS (svptr->AdminService) REPORT_ENDIS (svptr->NoLog) REPORT_STRING_DEFAULT (svptr->LogFormat) if (svptr->LogFormatProblem) *vecptr++ = "  (PROBLEM)"; else *vecptr++ = ""; REPORT_STRING_DEFAULT (svptr->BodyTag) if (svptr->ServiceErrorReportPath) { *vecptr++ = "!AZ !AZ"; *vecptr++ = svptr->ErrorReportPath; *vecptr++ = svptr->ErrorReportPathCodesPtr; } else *vecptr++ = ReportDefault; if (svptr->LogFileNameLength) { *vecptr++ = "!AZ (!&?open\rclosed\r)"; *vecptr++ = ADMIN_REPORT_SERVICE_LOG; *vecptr++ = svptr->ServerHostPort; if (LoggingPerService) { *vecptr++ = svptr->LogFileName; *vecptr++ = svptr->LogFileOpen; } else { slptr = smptr->ServiceListPtr; *vecptr++ = ((SERVICE_STRUCT*)LIST_GET_HEAD(slptr))->LogFileName; *vecptr++ = ((SERVICE_STRUCT*)LIST_GET_HEAD(slptr))->LogFileOpen; } } else *vecptr++ = "(none)"; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReportOtherFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } /**************/ /* end report */ /**************/ status = FaoToNet (rqptr, EndPageFao); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); #undef REPORT_ENDIS #undef REPORT_STRING_DEFAULT #undef REPORT_STRING_NONE } /*****************************************************************************/ /* A server administration menu for service configuration. This function just wraps the revision function, loading a temporary database if necessary for reporting from the configuration file. */ ServiceConfigRevise ( REQUEST_STRUCT *rqptr, BOOL UseServerDatabase ) { int status; META_CONFIG *mcptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceConfigRevise() !&F !UL", &ServiceConfigRevise, UseServerDatabase); if (UseServerDatabase) ServiceConfigReviseNow (rqptr, MetaGlobalServicePtr); else { status = ServiceConfigLoad (&mcptr); if (VMSnok (status)) { /* severe error reported */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI); } else ServiceConfigReviseNow (rqptr, mcptr); MetaConUnload (&mcptr, NULL); } AdminEnd (rqptr); } /*****************************************************************************/ /* Return a report on the HTTPd service configuration ... now! */ ServiceConfigReviseNow ( REQUEST_STRUCT *rqptr, META_CONFIG *mcptr ) { static char ReviseServiceNoneFao [] = "(no services defined)"; static char ReviseServiceFao [] = "!AZ\

\n\ \n\ \n\
!&@!AZ!AZ!AZ
\n\ \n\ \ \n\ \n\ \n\ \n\ \n\ \ \n"; static char ReviseHttp2Fao [] = "\n\ \ \n"; static char ReviseSslServiceFao [] = "\n\ \ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; static char ReviseProxyFao [] = "\n\ \ \n"; static char ReviseProxyDetailsFao [] = "\n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n"; static char ReviseSslClientFao [] = "\n\ \ \n"; static char ReviseSslClientDetailsFao [] = "\n\ \n\ \n\ \n\ \n\ \n\ \n"; static char ReviseOtherFao [] = "\n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n\ \ \n\ \
Scheme:\n\ \n\ http\n\ https\n\
Name:\ \
Port:\ \n\ \n\
Bind IP Address:\ \n\ \n\  (only if NI multi-homed)
HTTP2:\ \n\ enabled\n\ disabled\n\
SSL Version:\ \n\ \n\
SSL Options:\ \n\ \n\
SSL Cipher List:\ \n\ \n\
SSL Cipher Suites:\ \n\ \n\
SSL Certificate:\ \n\ \n\
SSL Private Key:\ \n\ \n\
SSL Session Lifetime:\ \n\ \n\ (hh:mm:ss)
SSL Verify Peer:\ \n\ enabled\n\ optional\n\ disabled\n\
SSL Verify Peer Data Max:\ \n\ \n\ (kBytes)
SSL Verify Peer CA File:\ \n\ \n\
SSL Verify Peer Depth:\ \n\ \n\
Strict Transport Security:\ \n\ \n\
Non-SSL Redirect:\ \n\ \n\
Proxy:\ \n\ enabled\n\ disabled\n\
Proxy Tunnel:\ \n\ none\n\ CONNECT\n\ FIREWALL\n\ RAW\n\
Proxy Bind IP Address:\ \n\ \n\  (only if NI multi-homed)
Proxy Affinity:\ \n\ enabled\n\ disabled\n\
Proxy Authorization:\ \n\ none\n\ PROXY\n\ CHAIN\n\ LOCAL\n\
Proxy Chain:\ \n\ \n\
Proxy Chain Cred:\ \n\ \
Proxy Rework Max:\ \n\ \n\ (kBytes)
SSL/Client:\ \n\ enabled\n\ disabled\n\
SSL/Client Version:\ \n\ \n\
SSL Certificate:\ \n\ \n\
SSL Private Key:\ \n\ \n\
SSL/Client Verify CA:\ \n\ enabled\n\ disabled\n\
SSL/Client Verify CA File:\ \n\ \n\
SSL/Client Cipher List:\ \n\ \n\
SSL/Client Cipher Suites:\ \n\ \n\
RawSocket:\ \n\ enabled\n\ disabled\n\
(On) Connect:\ \n\ enabled\n\ disabled\n\
Share SSH:\ \n\ \n\
Admin:\ \n\ enabled\n\ disabled\n\
NoLog:\ \n\ enabled\n\ disabled\n\
Log Format:\n\ \n\ \n\
Body Tag:\n\ \n\ \n\
Error Report Path:\n\ \n\ \n\
\n\
\n\ \n"; static char NotNone [] = "(none)\n"; int count, idx, status; unsigned long *vecptr; unsigned long FaoVector [128]; char *cptr, *sptr, *zptr; SERVICE_META *smptr; SERVICE_STRUCT *svptr; SESOLA_CONTEXT *scptr, *ssptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SERVICE)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SERVICE, "ServiceConfigReviseNow()"); /* get a pointer to the meta-config data */ smptr = mcptr->ServiceMetaPtr; AdminPageTitle (rqptr, "Service Configuration"); AdminMetaConReport (rqptr, mcptr, MetaGlobalServicePtr); if (ServiceConfigFileDefined) { AdminMetaConSource (rqptr, mcptr, MetaGlobalServicePtr); AdminMetaConBeginUpdateForm (rqptr); } else { status = FaoToNet (rqptr, "

"); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } /************/ /* services */ /************/ if (!LIST_GET_HEAD(smptr->ServiceListPtr)) { status = FaolToNet (rqptr, ReviseServiceNoneFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } else { count = 0; LIST_ITERATE (svptr, smptr->ServiceListPtr) { count++; vecptr = FaoVector; /* place holder for the "next new service" comment (see below) */ *vecptr++ = ""; *vecptr++ = "!UL  -  "; *vecptr++ = count; *vecptr++ = svptr->RequestSchemeNamePtr; *vecptr++ = "//"; *vecptr++ = svptr->ServerHostPort; *vecptr++ = count; *vecptr++ = svptr->RequestScheme == SCHEME_HTTP; *vecptr++ = count; *vecptr++ = svptr->RequestScheme == SCHEME_HTTPS; *vecptr++ = count; if (svptr->GenericService) *vecptr++ = "*"; else *vecptr++ = svptr->ServerHostName; *vecptr++ = count; *vecptr++ = svptr->ServerPortString; *vecptr++ = count; *vecptr++ = svptr->BindIpAddressString; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->Http2Enabled; *vecptr++ = count; *vecptr++ = !svptr->Http2Enabled; status = FaolToNet (rqptr, ReviseHttp2Fao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (ssptr = svptr->SSLserverPtr) { vecptr = FaoVector; *vecptr++ = count; *vecptr++ = ssptr->VersionString; *vecptr++ = count; *vecptr++ = ssptr->OptionsString; *vecptr++ = count; *vecptr++ = ssptr->CipherList; *vecptr++ = count; *vecptr++ = ssptr->CipherSuites; *vecptr++ = count; *vecptr++ = ssptr->CertFile; *vecptr++ = count; *vecptr++ = ssptr->KeyFile; *vecptr++ = count; *vecptr++ = MetaConShowSeconds (rqptr, ssptr->SessionLifetime); *vecptr++ = count; *vecptr++ = ssptr->VerifyPeer == SESOLA_VERIFY_PEER_REQUIRED; *vecptr++ = count; *vecptr++ = ssptr->VerifyPeer == SESOLA_VERIFY_PEER_OPTIONAL; *vecptr++ = count; *vecptr++ = !ssptr->VerifyPeer; *vecptr++ = count; *vecptr++ = ssptr->VerifyDataMax; *vecptr++ = count; *vecptr++ = ssptr->CaFile; *vecptr++ = count; *vecptr++ = ssptr->VerifyDepth; *vecptr++ = count; *vecptr++ = ssptr->StrictTransSec; *vecptr++ = count; *vecptr++ = svptr->NonSslRedirect; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseSslServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->ProxyService; *vecptr++ = count; *vecptr++ = !svptr->ProxyService; status = FaolToNet (rqptr, ReviseProxyFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (svptr->ProxyService) { vecptr = FaoVector; *vecptr++ = count; *vecptr++ = !svptr->ProxyTunnel; *vecptr++ = count; *vecptr++ = (svptr->ProxyTunnel == PROXY_TUNNEL_CONNECT); *vecptr++ = count; *vecptr++ = (svptr->ProxyTunnel == PROXY_TUNNEL_FIREWALL); *vecptr++ = count; *vecptr++ = (svptr->ProxyTunnel == PROXY_TUNNEL_RAW); *vecptr++ = count; *vecptr++ = svptr->ProxyBindIpAddressString; *vecptr++ = count; *vecptr++ = svptr->ProxyAffinity; *vecptr++ = count; *vecptr++ = !svptr->ProxyAffinity; *vecptr++ = count; *vecptr++ = !(svptr->ProxyAuthRequired || svptr->ProxyChainAuthRequired || svptr->LocalAuthRequired); *vecptr++ = count; *vecptr++ = svptr->ProxyAuthRequired; *vecptr++ = count; *vecptr++ = svptr->ProxyChainAuthRequired; *vecptr++ = count; *vecptr++ = svptr->LocalAuthRequired; *vecptr++ = count; *vecptr++ = svptr->ProxyChainHostPort; *vecptr++ = count; *vecptr++ = svptr->ProxyChainCred; *vecptr++ = svptr->ProxyReworkMax; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseProxyDetailsFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->SSLclientEnabled; *vecptr++ = count; *vecptr++ = !svptr->SSLclientEnabled; status = FaolToNet (rqptr, ReviseSslClientFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (scptr = svptr->SSLclientPtr) { vecptr = FaoVector; *vecptr++ = count; *vecptr++ = scptr->VersionString; *vecptr++ = count; *vecptr++ = scptr->CertFile; *vecptr++ = count; *vecptr++ = scptr->KeyFile; *vecptr++ = count; *vecptr++ = scptr->VerifyCA; *vecptr++ = count; *vecptr++ = !scptr->VerifyCA; *vecptr++ = count; *vecptr++ = scptr->CaFile; *vecptr++ = count; *vecptr++ = scptr->CipherList; *vecptr++ = count; *vecptr++ = scptr->CipherSuites; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseSslClientDetailsFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } vecptr = FaoVector; *vecptr++ = count; *vecptr++ = svptr->RawSocket; *vecptr++ = count; *vecptr++ = !svptr->RawSocket; *vecptr++ = count; *vecptr++ = svptr->ConnectService; *vecptr++ = count; *vecptr++ = !svptr->ConnectService; *vecptr++ = count; *vecptr++ = svptr->ShareSSH; *vecptr++ = count; *vecptr++ = svptr->AdminService; *vecptr++ = count; *vecptr++ = !svptr->AdminService; *vecptr++ = count; *vecptr++ = svptr->NoLog; *vecptr++ = count; *vecptr++ = !svptr->NoLog; *vecptr++ = count; *vecptr++ = svptr->LogFormat; *vecptr++ = count; *vecptr++ = svptr->BodyTag; *vecptr++ = count; if (svptr->ServiceErrorReportPath) { *vecptr++ = "!AZ !AZ"; *vecptr++ = svptr->ErrorReportPath; *vecptr++ = svptr->ErrorReportPathCodesPtr; } else *vecptr++ = ""; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseOtherFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } /***********************/ /* "blank" new service */ /***********************/ count++; vecptr = FaoVector; *vecptr++ = "\n"; *vecptr++ = ""; *vecptr++ = "- Next New Service -"; *vecptr++ = ""; *vecptr++ = ""; *vecptr++ = count; *vecptr++ = true; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = "?.?.?.?"; *vecptr++ = count; *vecptr++ = "80"; *vecptr++ = count; *vecptr++ = ""; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseServiceFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = FaoVector; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = true; status = FaolToNet (rqptr, ReviseProxyFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = FaoVector; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = true; *vecptr++ = count; *vecptr++ = 0; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = true; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = true; *vecptr++ = count; *vecptr++ = false; *vecptr++ = count; *vecptr++ = ""; *vecptr++ = count; *vecptr++ = ""; FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI); status = FaolToNet (rqptr, ReviseOtherFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /**************/ /* end report */ /**************/ if (ServiceConfigFileDefined) AdminMetaConEndUpdateForm (rqptr); else status = FaolToNet (rqptr, "\n", NULL); status = FaolToNet (rqptr, "\n\n", NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); } /*****************************************************************************/