/*****************************************************************************/ /* proxyMUNGE.c Munges (rewrites) URIs/URLs to provide an effective reverse-proxy, including: o response header "Location:" field o response header "Set-Cookie:" field path and domain components o HTML document * "href=" tag attributes * "src=" tag attributes * "background=" tag attributes * "action=" (form) tag attributes * "code=" (applet, object) tag attributes * "codebase=" (applet, object) tag attributes * "archive=" (object) tag attributes * "data=" (object) tag attributes * "usemap=" (object) tag attributes o HTML tag attributes must be in the form * attrib="data" * attrib='data' * attrib=data o HTML "style=" attribute * as per CSS document o CSS document * url("..") and url(..) property Does not munge JavaScript. Supports reverse-proxy to http:// and https:// (if built against SSL). Operates as a CGIplus or standard CGI script. CONFIGURATION ------------- To avoid odd behaviours some care must be taken to include all the relevant subtleties of this configuration (the '?', 'script=query-relaxed'). # HTTPD$MAP [[the.reverse-proxy.service:port]] redirect /whatever/* /proxymunge/http://the.internal.host/*? set /proxymunge/http://the.internal.host/* script=params=\ (REVERSE="/whatever",HOST="another.internal.host=/another",\ DOMAIN="internal.host.name=external.host.name") script+ /proxymunge/* /cgi-bin/proxymunge/* map=once script=query=relaxed # HTTPD$AUTH [opaque] /proxymunge/* r+w The REVERSE parameter in the configuration below is used to rewrite URLs/URIs in HTML documents returned to the client so that they may be accessed via the proxy script. A single REVERSE parameter must be supplied with each activation. The HOST parameter is used to replace the host[:port] component of a URL with the specified string (might be another reverse-proxy path). The HOST parameter may contain multiple 'match=substitute' by comma-separation. script=params=HOST=("one=two","three=four") The DOMAIN parameter is used to replace the domain component of a "Set-Cookie:" response header field. The DOMAIN parameter may contain multiple 'match=substitute' by comma-separation. script=params=DOMAIN=("one=two","three=four") This second example just illustrates how to extend the basic configuration to support reverse-proxies into multiple internal hosts. # HTTPD$MAP [[the.reverse-proxy.service:port]] redirect /one/* /proxymunge/http://one.internal.host/*? redirect /two/* /proxymunge/http://two.internal.host/*? # set /proxymunge/http://one.internal.host/* script=params=\ (REVERSE="/one",HOST="two.internal.host=/two",\ DOMAIN="one.host.name=external.host.name") # set /proxymunge/http://two.internal.host/* script=params=\ (REVERSE="/two",HOST="one.internal.host=/one",\ DOMAIN="two.host.name=external.host.name") # script+ /proxymunge/* /cgi-bin/proxymunge/* map=once script=query=relaxed COMMAND-LINE PARAMETERS ----------------------- /DBUG low level debug statements /WATCH output WASD server WATCH-able statements /SSLV23 support SSL version 2 or 3 (default is 3 only) (no longer supported with OpenSSL 1.0.n and later) /TLS1 support TLSv1 (deprecated with OpenSSL 1.1.n) /TLS11 support TLSv1.1 (OpenSSL 1.0.n and later) /TLS12 support TLSv1.2 (OpenSSL 1.0.n and later) /TLS13 support TLSv1.3 (OpenSSL 1.1.1 and later) (if multiple /TLS qualifiers then lowest and highest become range) LOGICAL NAMES ------------- PROXYMUNGE$DBUG same as /DBUG PROXYMUNGE$WATCH same as /WATCH PROXYMUNGE$PARAM same as CLI BUILD DETAILS ------------- Compile then link: $ @BUILD_PROXYMUNGE BUILD [] To just link: $ @BUILD_PROXYMUNGE LINK [] COPYRIGHT --------- Copyright (C) 2007-2020 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2. VERSION HISTORY (update SOFTWAREVN as well) --------------- 25-FEB-2020 MGD v2.1.0, add /TLS.. qualifiers 23-FEB-2020 MGD v2.0.0, adapt to OpenSSL v1.1.n and later 22-SEP-2007 MGD v1.0.0, initial development */ /*****************************************************************************/ #define COPYRIGHT_DATE "2007-2020" #define SOFTWAREVN "2.1.0" #define SOFTWARENM "PROXYMUNGE" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #define SOFTWARECR "Copyright (C) " COPYRIGHT_DATE " Mark G.Daniel" #ifndef MUNGE_VIA_SSL /* default is to build without SSL support */ #define MUNGE_VIA_SSL 0 #endif #ifndef __VAX # pragma nomember_alignment #endif /* standard C header files */ #include #include #include #include #include #include /* Internet-related header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include /* OpenSSL header files */ #if MUNGE_VIA_SSL #include #include #include #endif /* application header files */ #include #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define BOOL int #define TRUE 1 #define FALSE 0 #define FOUTS(from,to) { \ if (to > from) { \ char ch; \ ch = *(to); \ *(to) = '\0'; \ fputs (from,stdout); \ *(to) = ch; \ } \ } /******************/ /* global storage */ /******************/ char CopyrightDate [] = COPYRIGHT_DATE, SoftwareCopy [] = SOFTWARECR, Utility [] = "PROXYMUNGE"; BOOL CliSSLv23, CliTLSv1, CliTLSv11, CliTLSv12, CliTLSv13, Debug, HttpScheme, HttpsScheme, IsCgiPlus, ResponseContentOpaque, ResponseContentTextHtml, ResponseContentTextCss, WatchEnabled; int CgiGatewayMrs, CgiPlusUsageCount, CgiParamDomainLength, CgiParamInternalLength, CgiParamReverseLength, ReadBufferCount, ReadBufferSize, ResponseBodyCount, ResponseBodySize, ResponseContentLength, ResponseHeaderCount, ResponseHeaderSize, ResponseBodyLength, ServerSocket, ServerPortNumber; struct sockaddr_in ServerSockAddr; char *CgiContentLengthPtr, *CgiContentTypePtr, *CgiEnvironmentPtr, *CgiPathInfoPtr, *CgiQueryStringPtr, *CgiRequestMethodPtr, *CgiParamDomainPtr, *CgiParamHostPtr, *CgiParamReversePtr, *CgiGatewayMrsPtr, *MungeDomainPtr, *MungeHostPtr, *ReadBufferPtr, *RequestUriPtr, *ResponseHeaderPtr, *ResponseBodyPtr; char ServerHostName [128], ServerHostPort [128+16], ServerPort [16], SoftwareEnv [128]; #if MUNGE_VIA_SSL #define OPENSSL_SINCE_111 (OPENSSL_VERSION_NUMBER >= 0x10101000L) #define OPENSSL_BEFORE_111 (OPENSSL_VERSION_NUMBER < 0x10101000L) #define OPENSSL_SINCE_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L) #define OPENSSL_BEFORE_110 (OPENSSL_VERSION_NUMBER < 0x10100000L) SSL_CTX *SslContext; SSL *SslServer; SSL_METHOD *SslMethod; BIO *BioServer; #endif /***********************/ /* function prototypes */ /***********************/ char* GetCgiVar (char*); char* GetCgiVarNull (char*); void ErrorExit (int, int); void GetParameters (); void MungeCssText (char*, char*); char* MungeDomain (char*); char* MungeUrl (char*, char); void ProcessRequest (); void ReturnCssResponseBody (); void ReturnHtmlResponseBody (); void ReturnHttpResponseHeader (); char* SpanQuoted (char*, char*); char* SpanToEndTag (char*, char*, char*); char* SpanToTagClose (char*, char*); char* SysTrnLnm (char*, char*); char* StatTimer (BOOL); int WatchThis (int, char*, ...); int WatchDump (char*, int); BOOL strsame (char*, char*, int); /*****************************************************************************/ /* */ main () { int minTLS, maxTLS; char *cptr; unsigned char RandBits[32]; /*********/ /* begin */ /*********/ CgiEnvironmentPtr = CgiLibEnvironmentVersion(); sprintf (SoftwareEnv, "%s, %s, %s", SOFTWAREID, CgiEnvironmentPtr, BUILD_DATETIME); Debug = (SysTrnLnm ("PROXYMUNGE$DBUG", NULL) != NULL); if (Debug) { fprintf (stdout, "Content-Type: text/plain\nScript-Control: X-content-encoding-gzip=0\n\n"); CgiLibEnvironmentSetDebug (1); } GetParameters (); CgiLibEnvironmentInit (0, NULL, FALSE); IsCgiPlus = CgiLibEnvironmentIsCgiPlus (); #if MUNGE_VIA_SSL SSL_library_init (); OpenSSL_add_all_algorithms(); SSL_load_error_strings (); /* http://www.openssl.org/support/faq.html 'Why do I get a "PRNG not seeded" error message?' */ sys$gettim (&RandBits); sys$numtim (&RandBits[8], &RandBits); memmove (&RandBits[sizeof(RandBits)/2], &RandBits[3], sizeof(RandBits)/2); #if OPENSSL_SINCE_110 /* 23-FEB-2020 MGD linking against v1.1.1c (at least) required this */ #pragma names save #pragma names as_is RAND_seed (&RandBits, sizeof(RandBits)); #pragma names restore ERR_clear_error(); SslMethod = (SSL_METHOD*)TLS_client_method(); SslContext = SSL_CTX_new (SslMethod); if (!SslContext) { fprintf (stdout, "Status: 500\n\n[line:%d] ", __LINE__); ERR_print_errors_fp (stdout); ErrorExit (SS$_BUGCHECK, __LINE__); } minTLS = TLS1_1_VERSION; #if OPENSSL_SINCE_111 maxTLS = TLS1_3_VERSION; #else maxTLS = TLS1_2_VERSION; #endif if (CliTLSv1) minTLS = TLS1_VERSION; else if (CliTLSv11) minTLS = TLS1_1_VERSION; else if (CliTLSv12) minTLS = TLS1_2_VERSION; #if OPENSSL_SINCE_111 else if (CliTLSv13) minTLS = TLS1_3_VERSION; #endif #if OPENSSL_SINCE_111 if (CliTLSv13) maxTLS = TLS1_3_VERSION; else #endif if (CliTLSv12) maxTLS = TLS1_2_VERSION; else if (CliTLSv11) maxTLS = TLS1_1_VERSION; else if (CliTLSv1) maxTLS = TLS1_VERSION; ERR_clear_error(); if (!SSL_CTX_set_min_proto_version (SslContext, minTLS)) { fprintf (stdout, "Status: 500\n\n[line:%d] ", __LINE__); ERR_print_errors_fp (stdout); ErrorExit (SS$_BUGCHECK, __LINE__); return; } ERR_clear_error(); if (!SSL_CTX_set_max_proto_version(SslContext, maxTLS)) { fprintf (stdout, "Status: 500\n\n[line:%d] ", __LINE__); ERR_print_errors_fp (stdout); ErrorExit (SS$_BUGCHECK, __LINE__); return; } sprintf (SoftwareEnv, "%s, %s, %s, %s", SOFTWAREID, CgiEnvironmentPtr, OpenSSL_version (OPENSSL_VERSION), BUILD_DATETIME); #else /* OPENSSL_SINCE_110 */ RAND_seed (&RandBits, sizeof(RandBits)); if (CliSSLv23) SslMethod = (SSL_METHOD*)SSLv23_client_method (); else SslMethod = (SSL_METHOD*)SSLv3_client_method (); SslContext = SSL_CTX_new (SslMethod); if (!SslContext) ErrorExit (SS$_BUGCHECK, __LINE__); sprintf (SoftwareEnv, "%s, %s, %s, %s", SOFTWAREID, CgiEnvironmentPtr, SSLeay_version(SSLEAY_VERSION), BUILD_DATETIME); #endif /* OPENSSL_SINCE_110 */ #endif /* MUNGE_VIA_SSL */ for (;;) { /* block waiting for the next request */ CgiLibVar (""); if (WatchEnabled = (SysTrnLnm ("PROXYMUNGE$WATCH", NULL) != NULL)) WatchThis (__LINE__, NULL); ProcessRequest (); if (WatchEnabled) WatchThis (__LINE__, NULL); if (!IsCgiPlus) break; CgiLibCgiPlusEOF (); } } /*****************************************************************************/ /* */ void ProcessRequest () { int cnt, retval; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); if (WatchEnabled) WatchThis (__LINE__, "!AZ", SoftwareEnv); if (IsCgiPlus) { CgiPlusUsageCount++; if (WatchEnabled) WatchThis (__LINE__, "CgiPlus: !UL", CgiPlusUsageCount); } if (!ReadBufferPtr) { CgiGatewayMrsPtr = GetCgiVar ("GATEWAY_MRS"); CgiGatewayMrs = atoi(CgiGatewayMrsPtr); if (!CgiGatewayMrs) CgiGatewayMrs = 4096; ReadBufferSize = CgiGatewayMrs; ReadBufferPtr = calloc (1, ReadBufferSize); if (!ReadBufferPtr) ErrorExit (vaxc$errno, __LINE__); ResponseHeaderSize = CgiGatewayMrs; ResponseHeaderPtr = calloc (1, ResponseHeaderSize); if (!ResponseHeaderPtr) ErrorExit (vaxc$errno, __LINE__); } if (WatchEnabled) WatchThis (__LINE__, "GATEWAY_MRS !SL", CgiGatewayMrs); /********************/ /* munge parameters */ /********************/ CgiParamReversePtr = GetCgiVar ("REVERSE"); CgiParamReverseLength = strlen(CgiParamReversePtr); CgiParamHostPtr = GetCgiVar ("HOST"); CgiParamInternalLength = strlen(CgiParamHostPtr); if (MungeHostPtr) free (MungeHostPtr); MungeHostPtr = NULL; if (CgiParamInternalLength) { MungeHostPtr = sptr = calloc (1, CgiParamInternalLength+4); if (!MungeHostPtr) ErrorExit (vaxc$errno, __LINE__); for (cptr = CgiParamHostPtr; *cptr; *sptr++ = *cptr++); *(unsigned long*)sptr = 0; for (sptr = MungeHostPtr; *sptr; sptr++) if (*sptr == '=' || *sptr == ',') *sptr = '\0'; if (WatchEnabled) { for (sptr = MungeHostPtr; *sptr; sptr++) { for (cptr = sptr; *cptr; cptr++); cptr++; WatchThis (__LINE__, "URL host \'!AZ\' to \'!AZ\'", sptr, cptr); while (*cptr) cptr++; sptr = cptr; } } } CgiParamDomainPtr = GetCgiVar ("DOMAIN"); CgiParamDomainLength = strlen(CgiParamDomainPtr); if (MungeDomainPtr) free (MungeDomainPtr); MungeDomainPtr = NULL; if (CgiParamDomainLength) { MungeDomainPtr = sptr = calloc (1, CgiParamDomainLength+4); if (!MungeDomainPtr) ErrorExit (vaxc$errno, __LINE__); for (cptr = CgiParamDomainPtr; *cptr; *sptr++ = *cptr++); *(unsigned long*)sptr = 0; for (sptr = MungeDomainPtr; *sptr; sptr++) if (*sptr == '=' || *sptr == ',') *sptr = '\0'; if (WatchEnabled) { for (sptr = MungeDomainPtr; *sptr; sptr++) { for (cptr = sptr; *cptr; cptr++); cptr++; WatchThis (__LINE__, "COOKIE domain \'!AZ\' to \'!AZ\'", sptr, cptr); while (*cptr) cptr++; sptr = cptr; } } } /**********************/ /* parse host and URI */ /**********************/ CgiPathInfoPtr = GetCgiVar ("PATH_INFO"); CgiQueryStringPtr = GetCgiVar ("QUERY_STRING"); cptr = CgiPathInfoPtr; if (*cptr) cptr++; HttpScheme = HttpsScheme = FALSE; if (!strncmp (cptr, "http://", cnt = 7)) HttpScheme = TRUE; else if (!strncmp (cptr, "https://", cnt = 8)) HttpsScheme = TRUE; #if !MUNGE_VIA_SSL if (HttpsScheme) { if (WatchEnabled) WatchThis (__LINE__, "SSL not supported"); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'SSL not supported\'\n\n"); return; } #endif if (!(HttpScheme || HttpsScheme)) { if (WatchEnabled) WatchThis (__LINE__, "CONFUSED mapping !AZ", cptr); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'CONFUSED mapping\'\n\n"); return; } zptr = (sptr = ServerHostName) + sizeof(ServerHostName)-1; cptr += cnt; while (*cptr && *cptr != ':' && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != ':' && *cptr != '/') cptr++; if (*cptr == ':') { cptr++; zptr = (sptr = ServerPort) + sizeof(ServerHostName)-1; while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; ServerPortNumber = atoi(ServerPort); while (*cptr && *cptr != '/') cptr++; } else { #if MUNGE_VIA_SSL if (HttpsScheme) { strcpy (ServerPort, "443"); ServerPortNumber = 443; } else #endif { strcpy (ServerPort, "80"); ServerPortNumber = 80; } } sprintf (ServerHostPort, "%s:%s", ServerHostName, ServerPort); RequestUriPtr = cptr; /*****************/ /* proxy request */ /*****************/ retval = ConnectToServer (); #if MUNGE_VIA_SSL if (HttpsScheme && retval >= 0) { SslServer = SSL_new (SslContext); if (!SslServer) { if (WatchEnabled) { WatchThis (__LINE__, "SSL_new() failure"); ERR_print_errors_fp (stdout); } fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'SSL_new() failure\'\n\n"); return; } retval = SSL_set_fd (SslServer, ServerSocket); if (retval <= 0) { if (WatchEnabled) { WatchThis (__LINE__, "SSL_set_fd() failure"); ERR_print_errors_fp (stdout); } fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'SSL_set_fd() failure\'\n\n"); return; } ERR_clear_error(); retval = SSL_connect (SslServer); if (retval <= 0) { if (WatchEnabled) { WatchThis (__LINE__, "SSL_connect() failure"); ERR_print_errors_fp (stdout); } fprintf (stdout, "Status: 500\n\n[line:%d] ", __LINE__); ERR_print_errors_fp (stdout); return; } if (WatchEnabled) WatchThis (__LINE__, "SSL established using !AZ", SSL_get_cipher (SslServer)); } #endif if (retval >= 0) retval = MakeHttpRequest (); if (retval >= 0) retval = GetHttpResponse (); if (retval >= 0) { if (ResponseContentTextHtml) ReturnHtmlResponseBody (); else if (ResponseContentTextCss) ReturnCssResponseBody (); } #if MUNGE_VIA_SSL if (HttpsScheme) SSL_shutdown (SslServer); #endif shutdown (ServerSocket, 2); close (ServerSocket); #if MUNGE_VIA_SSL if (HttpsScheme) SSL_free (SslServer); #endif } /*****************************************************************************/ /* TCP/IP connect to the server specified by ServerHostName and ServerPortNumber. */ int ConnectToServer () { #define SIZINADDR (sizeof(struct in_addr)) int retval; struct in_addr InAddr; struct hostent *HostEntryPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ConnectToServer()\n"); if (WatchEnabled) WatchThis (__LINE__, "CONNECT !AZ", ServerHostPort); memset (&ServerSockAddr, 0, sizeof(ServerSockAddr)); ServerSockAddr.sin_family = AF_INET; ServerSockAddr.sin_port = htons (ServerPortNumber); InAddr.s_addr = inet_addr (ServerHostName); if (InAddr.s_addr == (unsigned int)INADDR_NONE) { HostEntryPtr = gethostbyname (ServerHostName); if (HostEntryPtr) memcpy (&ServerSockAddr.sin_addr, HostEntryPtr->h_addr, SIZINADDR); else { if (WatchEnabled) WatchThis (__LINE__, "host not resolved"); fprintf (stdout, "Status: 504\n\ Script-Control: X-error-text=\'host not resolved\'\n\n"); return (-1); } } else memcpy (&ServerSockAddr.sin_addr, &InAddr.s_addr, SIZINADDR); ServerSocket = socket(AF_INET, SOCK_STREAM, 0); if (ServerSocket < 0) { if (WatchEnabled) WatchThis (__LINE__, "SOCKET failure %X!8XL", vaxc$errno); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'%%X%08.08XL %s\'\n\n", vaxc$errno, strerror(vaxc$errno)); return (-1); } retval = connect (ServerSocket, (const struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr)); if (retval < 0) { if (WatchEnabled) WatchThis (__LINE__, "CONNECT failure %X!8XL", vaxc$errno); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'%%X%08.08XL %s\'\n\n", vaxc$errno, strerror(vaxc$errno)); return (retval); } } /****************************************************************************/ /* Rebuild an HTTP/1.0 request header representing the proxied request and write it to the TCP/IP connected server. */ int MakeHttpRequest () { int retval, ContentLength = 0, PostBufferCount = 0; char *aptr, *cptr, *sptr, *zptr, *PostBufferPtr = NULL; char RequestBuffer [2048]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MakeHttpRequest()\n"); CgiRequestMethodPtr = GetCgiVar ("REQUEST_METHOD"); zptr = (sptr = RequestBuffer) + sizeof(RequestBuffer)-1; for (cptr = CgiRequestMethodPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = ' '; for (cptr = RequestUriPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); if (*CgiQueryStringPtr) { if (sptr < zptr) *sptr++ = '?'; for (cptr = CgiQueryStringPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); } for (cptr = " HTTP/1.0\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "Host: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = ServerHostName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (ServerPortNumber != 80) { if (sptr < zptr) *sptr++ = ':'; for (cptr = ServerPort; *cptr && sptr < zptr; *sptr++ = *cptr++); } for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "X-Proxy-Agent: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = SoftwareEnv; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); aptr = GetCgiVarNull ("HTTP_AUTHORIZATION"); if (aptr) { for (cptr = "Authorization: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_USER_AGENT"); if (aptr) { for (cptr = "User-Agent: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_ACCEPT"); if (aptr) { for (cptr = "Accept: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_ACCEPT_CHARSET"); if (aptr) { for (cptr = "Accept-Charset: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_LANGUAGE"); if (aptr) { for (cptr = "Accept-Language: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_COOKIE"); if (aptr) { for (cptr = "Cookie: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_IF_MODIFIED_SINCE"); if (aptr) { for (cptr = "If-Modified-Since: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (!strcmp (CgiRequestMethodPtr, "GET")) { /* nothing additional required with this supported method */ } else if (!strcmp (CgiRequestMethodPtr, "HEAD")) { /* nothing additional required with this supported method */ } else if (!strcmp (CgiRequestMethodPtr, "POST")) { CgiContentTypePtr = GetCgiVar("CONTENT_TYPE"); CgiContentLengthPtr = GetCgiVar("CONTENT_LENGTH"); ContentLength = atoi(CgiContentLengthPtr); CgiLibReadRequestBody (&PostBufferPtr, &PostBufferCount); if (WatchEnabled) WatchThis (__LINE__, "POST !UL bytes", PostBufferCount); if (!PostBufferPtr || PostBufferCount != ContentLength) { if (PostBufferPtr) free (PostBufferPtr); if (WatchEnabled) WatchThis (__LINE__, "POST body read failure"); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'POST body read failure\'\n\n"); return (-1); } for (cptr = "Content-Type: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = CgiContentTypePtr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\nContent-Length: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = CgiContentLengthPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } else { if (WatchEnabled) WatchThis (__LINE__, "!AZ not supported", CgiRequestMethodPtr); fprintf (stdout, "Status: 501\n\ Script-Control: X-error-text=\'%s not supported\'\n\n", CgiRequestMethodPtr); return (-1); } aptr = GetCgiVarNull ("HTTP_PRAGMA"); if (aptr) { for (cptr = "Pragma: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } aptr = GetCgiVarNull ("HTTP_CACHE_CONTROL"); if (aptr) { for (cptr = "Cache-Control: "; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = aptr; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); } for (cptr = "Connection: close\r\n\r\n"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (WatchEnabled) WatchThis (__LINE__, "!AZ", RequestBuffer); if (sptr >= zptr) { if (WatchEnabled) WatchThis (__LINE__, "Request buffer overflow"); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'Request buffer overflow\'\n\n"); return (-1); } #if MUNGE_VIA_SSL if (HttpsScheme) retval = SSL_write (SslServer, RequestBuffer, sptr-RequestBuffer); else #endif retval = write (ServerSocket, RequestBuffer, sptr-RequestBuffer); if (PostBufferPtr) { if (retval >= 0) { #if MUNGE_VIA_SSL if (HttpsScheme) SSL_write (SslServer, PostBufferPtr, PostBufferCount); else #endif write (ServerSocket, PostBufferPtr, PostBufferCount); } free (PostBufferPtr); } return (retval); } /****************************************************************************/ /* Read the response from the TCP/IP connected server. Continue reading until the HTTP response header is received. Once that is available parse the contents for information relevant to processing the body and send it to the client (via the script stdout) fields munged as necessary. If the response content-type is not HTML then it's opaque and continue to read, sending each buffer back to the client, until the server disconnects. If the response body is HTML then continue reading, buffering that body until the server disconnects. */ int GetHttpResponse () { int retval; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetHttpRequest()\n"); ResponseHeaderCount = 0; zptr = (sptr = ResponseHeaderPtr) + ResponseHeaderSize - 1; for (;;) { #if MUNGE_VIA_SSL if (HttpsScheme) retval = SSL_read (SslServer, ReadBufferPtr, ReadBufferSize); else #endif retval = read (ServerSocket, ReadBufferPtr, ReadBufferSize); if (WatchEnabled) { WatchThis (__LINE__, "READ !SL", retval); if (retval > 0) WatchDump (ReadBufferPtr, retval); } if (retval <= 0) break; ReadBufferCount = retval; if (!ResponseHeaderCount) { /*******************/ /* response header */ /*******************/ cptr = ReadBufferPtr; while (retval-- && sptr < zptr) { *sptr++ = *cptr; if (*cptr++ == '\n') { if (sptr > ResponseHeaderPtr+2 && *(unsigned short*)(sptr-2) == '\n\n') { *sptr = '\0'; ResponseHeaderCount = sptr - ResponseHeaderPtr; break; } else if (sptr > ResponseHeaderPtr+4 && *(unsigned long*)(sptr-4) == '\r\n\r\n') { *sptr = '\0'; ResponseHeaderCount = sptr - ResponseHeaderPtr; break; } } } if (!ResponseHeaderCount) continue; ReturnHttpResponseHeader (); /*************/ /* any body? */ /*************/ if (ResponseContentOpaque) { /* write anything remaining in the buffer following the header */ if (retval > 0) if (ReadBufferCount - ResponseHeaderCount > 0) retval = fwrite (ReadBufferPtr + ResponseHeaderCount, ReadBufferCount - ResponseHeaderCount, 1, stdout); } else if (ReadBufferCount - ResponseHeaderCount) { /* response body remaining after parse of response header */ if (ResponseContentLength) ResponseBodySize = ResponseContentLength; else ResponseBodySize = CgiGatewayMrs; ResponseBodyPtr = calloc (1, ResponseBodySize+16); if (!ResponseBodyPtr) ErrorExit (vaxc$errno, __LINE__); memcpy (ResponseBodyPtr, ReadBufferPtr + ResponseHeaderCount, ReadBufferCount - ResponseHeaderCount); ResponseBodyCount = ReadBufferCount - ResponseHeaderCount; } continue; } /*****************/ /* response body */ /*****************/ if (ResponseContentOpaque) { /*************************/ /* to client immediately */ /*************************/ fwrite (ReadBufferPtr, ReadBufferCount, 1, stdout); } else { /*******************/ /* buffer the body */ /*******************/ if (!ResponseBodyPtr) { if (ResponseContentLength) ResponseBodySize = ResponseContentLength; else ResponseBodySize = CgiGatewayMrs; ResponseBodyPtr = calloc (1, ResponseBodySize+16); if (!ResponseBodyPtr) ErrorExit (vaxc$errno, __LINE__); ResponseBodyCount = 0; } if (ResponseBodyCount + ReadBufferCount > ResponseBodySize) { ResponseBodySize += CgiGatewayMrs; ResponseBodyPtr = realloc (ResponseBodyPtr, ResponseBodySize+16); if (!ResponseBodyPtr) ErrorExit (vaxc$errno, __LINE__); } memcpy (ResponseBodyPtr+ResponseBodyCount, ReadBufferPtr, ReadBufferCount); ResponseBodyCount += ReadBufferCount; if (WatchEnabled) WatchThis (__LINE__, "BODY !UL/!UL", ResponseBodySize, ResponseBodyCount); } } return (retval); } /****************************************************************************/ /* Return a munged (if required) HTTP response header to the client. */ void ReturnHttpResponseHeader () { char *aptr, *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ReturnHttpResponseHeader()\n"); ResponseContentOpaque = TRUE; ResponseContentTextCss = ResponseContentTextHtml = FALSE; /* first, are we dealing with HTML? */ cptr = ResponseHeaderPtr; while (*cptr) { if (strsame (cptr, "Content-Type:", 13)) { for (cptr += 13; *cptr && *cptr == ' '; cptr++); if (!strncmp (cptr, "text/html", 9)) { ResponseContentTextHtml = TRUE; ResponseContentOpaque = FALSE; } else if (!strncmp (cptr, "text/css", 8)) { ResponseContentTextCss = TRUE; ResponseContentOpaque = FALSE; } break; } while (*cptr && *cptr != '\n') cptr++; if (*cptr) cptr++; } cptr = sptr = ResponseHeaderPtr; while (*cptr) { if (strsame (cptr, "Content-Length:", 15)) { if (ResponseContentOpaque) { /* opaque content, we'll just be spitting out a bago'bytes */ for (cptr += 15; *cptr && *cptr == ' '; cptr++); if (isdigit (*cptr)) ResponseContentLength = atoi(cptr); } else { /* once munged this content-length most likely won't be accurate */ FOUTS (sptr, cptr) for (cptr += 15; *cptr && *cptr != '\n'; cptr++); if (*cptr) cptr++; sptr = cptr; } } else if (strsame (cptr, "Set-Cookie:", 11)) { cptr += 11; while (*cptr && *cptr != '\n') { if (tolower(*cptr) == 'p' && strsame (cptr, "path=", 5)) { cptr += 5; FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, 0); } else if (tolower(*cptr) == 'd' && strsame (cptr, "domain=", 7)) { cptr += 7; FOUTS (sptr, cptr) cptr = sptr = MungeDomain (cptr); } else cptr++; } } else if (strsame (cptr, "Location:", 9)) { cptr += 9; while (*cptr && *cptr == ' ') cptr++; FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, 0); } while (*cptr && *cptr != '\n') cptr++; if (*cptr) cptr++; } FOUTS (sptr, cptr) } /****************************************************************************/ /* Process the HTML content of the response body, munging URLs contained in ACTION=, BACKGROUND=, HREF= and SRC= as required. Free the dynamically allocated body buffer when complete. */ void ReturnHtmlResponseBody () { char ch; char *aptr, *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ReturnHttpResponseBody()\n"); if (!ResponseBodyPtr) return; zptr = (cptr = sptr = ResponseBodyPtr) + ResponseBodyCount; while (cptr < zptr) { if (*cptr != '<') { /* not inside a markup tag */ cptr++; continue; } if (strsame (cptr, "", cptr, zptr); continue; } if (strsame (cptr, "", cptr, zptr); if (cptr < zptr) MungeCssText (aptr, (sptr=(cptr-8))); continue; } /*************/ /* OTHER TAG */ /*************/ cptr++; while (cptr < zptr) { /* if reached the end of a markup tag (or non-well-formed one) */ if (*cptr == '>' || *cptr == '<') break; if (*cptr == '\"' || *cptr == '\'') { cptr = SpanQuoted (cptr, zptr); continue; } if (tolower(*cptr) == 's' && strsame (cptr, "style=", 6)) { /*******************/ /* STYLE ATTRIBUTE */ /*******************/ cptr += 6; if (*cptr == '\"' || *cptr == '\'') { ch = *cptr++; FOUTS (sptr, (aptr = cptr)) while (cptr < zptr && *cptr != ch) cptr++; MungeCssText (aptr, (sptr=cptr)); if (cptr < zptr) cptr++; } else { FOUTS (sptr, (aptr = cptr)) while (cptr < zptr && !isspace(*cptr)) cptr++; MungeCssText (aptr, (sptr=cptr)); } continue; } /*******************/ /* OTHER ATTRIBUTE */ /*******************/ if (tolower(*cptr) == 'h' && strsame (cptr, "href=", 5)) cptr += 5; else if (tolower(*cptr) == 's' && strsame (cptr, "src=", 4)) cptr += 4; else if (tolower(*cptr) == 'b' && strsame (cptr, "background=", 11)) cptr += 11; else if (tolower(*cptr) == 'a' && strsame (cptr, "action=", 7)) cptr += 7; else if (tolower(*cptr) == 'c' && strsame (cptr, "code=", 5)) cptr += 5; else if (tolower(*cptr) == 'c' && strsame (cptr, "codebase=", 9)) cptr += 9; else if (tolower(*cptr) == 'd' && strsame (cptr, "data=", 5)) cptr += 5; else if (tolower(*cptr) == 'u' && strsame (cptr, "usemap=", 7)) cptr += 7; else if (tolower(*cptr) == 'a' && strsame (cptr, "archive=", 8)) cptr += 8; else { cptr++; continue; } if (*cptr == '\"' || *cptr == '\'') { ch = *cptr++; FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, ch); while (cptr < zptr && *cptr != ch) cptr++; if (cptr < zptr) cptr++; } else { FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, 0); } } } FOUTS (sptr, cptr) free (ResponseBodyPtr); ResponseBodyPtr = NULL; ResponseBodySize = 0; } /****************************************************************************/ /* Span all text between the quotes. */ char* SpanQuoted ( char *StartPtr, char *EndPtr ) { char ch; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SpanQuoted()\n"); cptr = StartPtr; ch = *cptr++; while (cptr < EndPtr && *cptr != ch) cptr++; if (cptr < EndPtr) cptr++; return (cptr); } /****************************************************************************/ /* Span all text between to the end-of-tag '>'. */ char* SpanToTagClose ( char *cptr, char *zptr ) { char ch; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SpanToTagClose()\n"); while (cptr < zptr) { if (*cptr == '\"' || *cptr == '\'') { cptr = SpanQuoted (cptr, zptr); continue; } /* if end of tag */ if (*cptr == '>') break; cptr++; } if (cptr < zptr) cptr++; return (cptr); } /****************************************************************************/ /* Span all text between to the specific end tag (e.g. ""). */ char* SpanToEndTag ( char *EndTag, char *cptr, char *zptr ) { int len; char ch; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SpanToEndTag()\n"); len = strlen (EndTag); while (cptr < zptr) { if (*cptr == '\"' || *cptr == '\'') { cptr = SpanQuoted (cptr, zptr); continue; } /* if end tag */ if (*cptr == '<' && strsame (cptr, EndTag, len)) break; cptr++; } if (cptr+len < zptr) cptr += len; return (cptr); } /****************************************************************************/ /* Munge a "text/css" response body. */ void ReturnCssResponseBody () { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ReturnCssResponseBody()\n"); if (!ResponseBodyPtr) return; MungeCssText (ResponseBodyPtr, ResponseBodyPtr+ResponseBodyCount); free (ResponseBodyPtr); ResponseBodyPtr = NULL; ResponseBodySize = 0; } /****************************************************************************/ /* About all that is required for munging Cascading Style Sheets is to identify the 'url' element and modify that appropriately. */ void MungeCssText ( char *cptr, char *zptr ) { char *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MungeCssText()\n"); sptr = cptr; while (cptr < zptr) { if (*cptr != '{') { /* not inside a property */ if (*(unsigned short*)cptr == '/*') { /* ingore anything inside comment delimiters */ cptr += 2; while (cptr < zptr && *(unsigned short*)cptr != '*/') cptr++; if (cptr < zptr) cptr += 2; } else cptr++; continue; } cptr++; while (cptr < zptr) { /* if reached the end of a property (or non-well-formed one) */ if (*cptr == '}' || *cptr == '{') break; /* ingore anything inside a property between double quotes */ if (*cptr == '\"') { cptr = SpanQuoted (cptr, zptr); continue; } /* inside a property */ if (tolower(*cptr) == 'u' && (!strncmp (cptr, "url(", 4) || !strncmp (cptr, "url ", 4))) cptr += 4; else { cptr++; continue; } while (*cptr && isspace(*cptr)) cptr++; if (*cptr == '\"') { cptr++; FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, '\"'); while (cptr < zptr && *cptr != '\"') cptr++; if (cptr < zptr) cptr++; } else { FOUTS (sptr, cptr) cptr = sptr = MungeUrl (cptr, 0); } } } FOUTS (sptr, cptr) } /****************************************************************************/ /* Modify a URL in a HREF=, SRC=, etc. If the URL is not a relative reference then check if it's an absolute URL. If so check if the host component if the same as the proxied-to server. If it is then just substitute the reverse-proxy path. If a rooted URI then just prefix it with the reverse-proxy path. */ char* MungeUrl ( char *UrlPtr, char QuoteChar ) { int cnt, retval; char *cptr, *sptr, *zptr; char HostBuffer [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MungeUrl()\n"); if (!*CgiParamReversePtr) return (UrlPtr); cptr = UrlPtr; if (strsame (cptr, "//", cnt = 2) || strsame (cptr, "http://", cnt = 7)) { cptr += cnt; zptr = (sptr = HostBuffer) + sizeof(HostBuffer)-1; while (*cptr && *cptr != '/' && *cptr != QuoteChar && !isspace(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '/' && *cptr != QuoteChar && !isspace(*cptr)) cptr++; if (sptr = MungeHostPtr) { while (*sptr) { if (strsame (HostBuffer, sptr, -1)) { while (*sptr) sptr++; sptr++; fputs (sptr, stdout); return (cptr); } while (*sptr) sptr++; sptr++; if (*sptr) { while (*sptr) sptr++; sptr++; } } } if (!strsame (HostBuffer, ServerHostName, -1) && !strsame (HostBuffer, ServerHostPort, -1)) return (UrlPtr); fputs (CgiParamReversePtr, stdout); return (cptr); } if (*cptr == '/') { /* rooted URI */ fputs (CgiParamReversePtr, stdout); return (cptr); } return (UrlPtr); } /****************************************************************************/ /* Rewrites the 'domain=' component of a cookie. */ char* MungeDomain (char *DomainPtr) { int cnt, retval; char *cptr, *sptr, *zptr; char DomainBuffer [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MungeDomain()\n"); if (!MungeDomainPtr) return (DomainPtr); cptr = sptr = DomainPtr; zptr = (sptr = DomainBuffer) + sizeof(DomainBuffer)-1; while (*cptr && *cptr != ';' && !isspace(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != ';' && !isspace(*cptr)) cptr++; sptr = MungeDomainPtr; while (*sptr) { if (strsame (DomainBuffer, sptr, -1)) { while (*sptr) sptr++; sptr++; fputs (sptr, stdout); return (cptr); } while (*sptr) sptr++; sptr++; if (*sptr) { while (*sptr) sptr++; sptr++; } } return (DomainPtr); } /*****************************************************************************/ /* */ char* GetCgiVar (char *VarName) { char *cptr; /*********/ /* begin */ /*********/ cptr = CgiLibVar (VarName); if (WatchEnabled) WatchThis (__LINE__, "!AZ=!AZ", VarName, cptr); return (cptr); } /*****************************************************************************/ /* */ char* GetCgiVarNull (char *VarName) { char *cptr; /*********/ /* begin */ /*********/ cptr = CgiLibVarNull (VarName); if (WatchEnabled) WatchThis (__LINE__, "!AZ=!AZ", VarName, cptr ? cptr : "(null)"); return (cptr); } /*****************************************************************************/ /* Exit after reporting the source code module name and line number. */ void ErrorExit ( int StatusValue, int SourceLineNumber ) { /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis (__LINE__, "%X!8XL - fatal error"); fprintf (stdout, "Status: 500\n\ Script-Control: X-error-text=\'%X!8XL - fatal error\'\n\n", StatusValue); exit (StatusValue); } /*****************************************************************************/ /* Hmmm, idea look familiar? :-) */ int WatchThis ( int SourceCodeLine, char *FaoString, ... ) { static int WatchCount; int argcnt, status; char *cptr, *sptr, *zptr; char Buffer [16384], FaoBuffer [256]; unsigned long *vecptr; $DESCRIPTOR (BufferDsc, Buffer); $DESCRIPTOR (FaoBufferDsc, FaoBuffer); unsigned long FaoVector [32]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (Debug) fprintf (stdout, "WatchThis() %d |%s|\n", argcnt, FaoString); if (!FaoString) { if (WatchCount) { /* post-processing reset */ WatchThis (__LINE__, "STATS !AZ", StatTimer(TRUE)); WatchThis (__LINE__, "WATCH end"); WatchEnabled = WatchCount = 0; } else { /* pre-processing initialize */ WatchCount = 1; CgiLibResponseHeader (200, "text/plain", "Script-Control: X-content-encoding-gzip=0\n"); WatchThis (__LINE__, "WATCH begin"); StatTimer(FALSE); } return (SS$_NORMAL); } vecptr = FaoVector; *vecptr++ = WatchCount++; *vecptr++ = 0; *vecptr++ = SourceCodeLine; va_start (argptr, FaoString); for (argcnt -= 2; argcnt; argcnt--) *vecptr++ = (unsigned long)va_arg (argptr, unsigned long); va_end (argptr); zptr = (sptr = FaoBuffer) + sizeof(FaoBuffer)-3; for (cptr = "|!4ZL|!%T|!4ZL|"; *cptr; *sptr++ = *cptr++); for (cptr = FaoString; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr++ = '|'; *sptr++ = '\n'; *sptr++ = '\0'; FaoBufferDsc.dsc$a_pointer = FaoBuffer; FaoBufferDsc.dsc$w_length = sptr - FaoBuffer; status = sys$faol (&FaoBufferDsc, 0, &BufferDsc, (unsigned long*)&FaoVector); if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); if (!(status & 1)) exit (status); fputs (Buffer, stdout); return (status); } /*****************************************************************************/ /* Ditto? :-) */ int WatchDump ( char *DataPtr, int DataLength ) { int cnt, len; char *cptr, *lptr, *sptr; char WatchBuffer [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WatchDump() %d\n", DataLength); cptr = DataPtr; len = DataLength; while (len) { sptr = WatchBuffer; lptr = cptr; cnt = 0; while (cnt < 32) { if (cnt < len) sptr += sprintf (sptr, "%02.02X", (unsigned char)*cptr++); else { *sptr++ = ' '; *sptr++ = ' '; } if (!(++cnt % 4)) *sptr++ = ' '; } *sptr++ = ' '; cptr = lptr; cnt = 0; while (cnt < 32 && cnt < len) { if (isprint(*cptr)) *sptr++ = *cptr; else *sptr++ = '.'; cptr++; cnt++; } if (len > 32) len -= 32; else len = 0; *sptr++ = '\n'; *sptr = '\0'; fputs (WatchBuffer, stdout); } return (1); } /*****************************************************************************/ /* */ char* StatTimer (BOOL ShowStat) { static $DESCRIPTOR (FaoDsc, "REAL:!%T CPU:!UL.!2ZL DIO:!UL BIO:!UL FAULTS:!UL\0"); static char StatString [96]; static $DESCRIPTOR (StatStringDsc, StatString); static unsigned long LibStatTimerReal = 1, LibStatTimerCpu = 2, LibStatTimerBio = 3, LibStatTimerDio = 4, LibStatTimerFaults = 5; int status; unsigned long CpuBinTime, CountBio, CountDio, CountFaults; unsigned long RealBinTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "StatTimer()\n"); if (!ShowStat) { lib$init_timer (0); return (NULL); } /* post-processing reset */ lib$stat_timer (&LibStatTimerReal, &RealBinTime, 0); lib$stat_timer (&LibStatTimerCpu, &CpuBinTime, 0); lib$stat_timer (&LibStatTimerBio, &CountBio, 0); lib$stat_timer (&LibStatTimerDio, &CountDio, 0); lib$stat_timer (&LibStatTimerFaults, &CountFaults, 0); sys$fao (&FaoDsc, 0, &StatStringDsc, &RealBinTime, CpuBinTime/100, CpuBinTime%100, CountDio, CountBio, CountFaults); return (StatString); } /*****************************************************************************/ /* Translate a logical name using LNM$FILE_DEV. Return a pointer to the value string, or NULL if the name does not exist. If 'LogValue' is supplied the logical name is translated into that (assumed to be large enough), otherwise it's translated into an internal static buffer. */ char* SysTrnLnm ( char *LogName, char *LogValue ) { static unsigned short ShortLength; static char StaticLogValue [256]; static $DESCRIPTOR (LogNameDsc, ""); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { 255, LNM$_STRING, 0, &ShortLength }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SysTrnLnm() |%s|\n", LogName); LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); if (LogValue) cptr = LnmItems[0].buf_addr = LogValue; else cptr = LnmItems[0].buf_addr = StaticLogValue; status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (!(status & 1)) { if (Debug) fprintf (stdout, "|(null)|\n"); return (NULL); } cptr[ShortLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", cptr); return (cptr); } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns TRUE if two strings are the same, or FALSE if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ BOOL strsame ( char *sptr1, char *sptr2, int count ) { /*********/ /* begin */ /*********/ while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (FALSE); if (count) if (!--count) return (TRUE); } if (*sptr1 || *sptr2) return (FALSE); else return (TRUE); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. OSU scripts have the 'method', 'url' and 'protocol' supplied as P1, P2, P3 (these being detected and used by CGILIB), and are of no interest to this function. */ void GetParameters () { static char CommandLine [1024]; static unsigned long Flags = 0; int status, SkipParameters; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetParameters()\n"); if ((clptr = getenv ("PROXYMUNGE$PARAM")) == NULL) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } /* if OSU environment then skip P1, P2, P3 */ if (getenv ("WWWEXEC_RUNDOWN_STRING") != NULL) SkipParameters = 3; else SkipParameters = 0; aptr = NULL; ch = *clptr; for (;;) { if (aptr != NULL && *aptr == '/') *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (SkipParameters) { SkipParameters--; continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = TRUE; continue; } if (strsame (aptr, "/SOFTWAREID", 4) || strsame (aptr, "/VERSION", 4)) { fprintf (stdout, "%%%s-I-SOFTWAREID, %s\n%s\n", Utility, SoftwareEnv, SoftwareCopy); exit (SS$_NORMAL); } #if OPENSSL_SINCE_110 if (strsame (aptr, "/TLS1", -1)) { CliTLSv1 = TRUE; continue; } if (strsame (aptr, "/TLS11", -1)) { CliTLSv11 = TRUE; continue; } if (strsame (aptr, "/TLS12", -1)) { CliTLSv12 = TRUE; continue; } #if OPENSSL_SINCE_111 if (strsame (aptr, "/TLS13", -1)) { CliTLSv13 = TRUE; continue; } #endif #endif #if OPENSSL_BEFORE_110 if (strsame (aptr, "/SSLV23", -1)) { CliSSLv23 = TRUE; continue; } #endif if (strsame (aptr, "/WATCH", 4)) { WatchEnabled = TRUE; continue; } if (*aptr == '/') { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } /*****************************************************************************/