blob: 3633cf3a04019588e98fe2e9a30d11b9714604b0 [file] [log] [blame]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "InkTime.h"
#include "Main.h"
#include "AutoConf.h"
#include "TextBuffer.h"
#include "Tokenizer.h"
#include "WebMgmtUtils.h"
#include "ink_assert.h"
#include "ExpandingArray.h"
#include "WebOverview.h"
/****************************************************************************
*
* AutoConf.cc - code to generate and delete client autoconf files
*
*
****************************************************************************/
#define FILE_MODE S_IRWXU
static const char fileHead[] = "function FindProxyForURL(url, host) {\n\n";
static const char checkProtocol[] =
"\t// Make sure this a protcol we proxy\n\tif(!((url.substring(0,5) == \"http:\") ||\n\t\t(url.substring(0,4) == \"ftp:\") || \n\t\t(url.substring(0,6) == \"https:\"))) {\n\t\t return \"DIRECT\";\n\t}\n\n";
/*
static const char checkProtocol[] = "\t// Make sure this a protcol we proxy\n\tif(!((url.substring(0,5) == \"http:\") || \n\t\t(url.substring(0,6) == \"https:\"))) {\n\t\t return \"DIRECT\";\n\t}\n\n";
*/
static const char checkNQ[] = "\tif(isPlainHostName(host)) {\n\t\t return \"DIRECT\";\n\t}\n\n";
static const char checkDomain[] = "dnsDomainIs(host, \"";
static const char checkHost[] = "localHostOrDomainIs(host, \"";
static const char directResponse[] = "\t\treturn \"DIRECT\";\n\t}\n\n";
static const char returnStr[] = "\n\treturn ";
static const char proxyName[] = "ink-proxy.inktomi.com";
static const char proxyStr[] = "\"PROXY ";
static const char directStr[] = "\"DIRECT\";";
AutoConf *autoConfObj;
const char *pacStrings[] = {
"Request Succeeded\n",
"No Client Auto Configuration Directory",
"Create of Client Auto Configuration File Failed\n",
"Invalid Submission\n",
"File Already Exists\n",
"Remove Failed\n",
"Missing File Name\n"
};
AutoConf::AutoConf()
{
}
AutoConf::~AutoConf()
{
}
void
AutoConf::displayAutoConfPage(textBuffer * output)
{
ExpandingArray acList(10, true);
const char docStart[] =
"<html>\n<head>\n<title> Browser Auto-Configuration </title>\n</head>\n<body bgcolor=\"#FFFFFF\">\n<h1> Configure: Browser Auto-Configuration </h1>\n";
const char docEnd[] =
"<a href=\"/main.ink?t=c_serv\" target=_top> <img src=\"/images/back.gif\" border=\"0\"> Configure: Server Basics </a>\n</body>\n</html>\n";
const char active1[] = "<p> An Auto-Configuration file exists.\n It was last modified at ";
const char active2[] =
"</p>\n <p><form method=GET action=\"/configure/autoconf_add.html\"><input type=Submit value=\"Replace the current file\" onClick=\"newWindow('proxy_pac_view')\"></form></p>\n<p><form method=POST action=autoconf_action.html>\n<input type=hidden name=action value=delete>\n<input type=submit value=\"Delete The current file\"></form></p>\n<p><form method=POST action=\"autoconf_action.html\"><input type=Submit value=\"View the current file\">\n<input type=hidden name=action value=view></form></p>\n <SCRIPT LANGUAGE=\"JavaScript\">function newWindow(winName) { \n window.open(\"/configure/autoconf_proxy_pac.html\", winName, \"width=680,height=420\"); }</SCRIPT> \n";
const char noFile[] =
"<p> There is no autoconfiguration file. <a href=\"/configure/autoconf_add.html\"> Create One </a></p>";
struct stat fileInfo;
bool pacFile = false;
Rollback *pacRoll;
char dateBuf[64];
// Check to see if we have a client autoconfig file already
//
// If the file is zero length, that means that it is not active
//
if (configFiles->getRollbackObj("proxy.pac", &pacRoll)) {
if (pacRoll->statVersion(ACTIVE_VERSION, &fileInfo) == 0) {
if (fileInfo.st_size > 0) {
pacFile = true;
}
}
}
output->copyFrom(docStart, strlen(docStart));
if (pacFile == true) {
output->copyFrom(active1, strlen(active1));
char *result = ink_ctime_r(&fileInfo.st_mtime, dateBuf);
if (result != NULL) {
output->copyFrom(dateBuf, strlen(dateBuf));
} else {
output->copyFrom("???", 3);
}
output->copyFrom(active2, strlen(active2));
} else {
output->copyFrom(noFile, strlen(noFile));
}
output->copyFrom(docEnd, strlen(docEnd));
}
void
AutoConf::byPass(textBuffer & newFile, Tokenizer & tok, const char *funcStr)
{
int num = tok.getNumber();
newFile.copyFrom("\tif(", 4);
for (int i = 0; i < num; i++) {
newFile.copyFrom(funcStr, strlen(funcStr));
newFile.copyFrom(tok[i], strlen(tok[i]));
if (i + 1 != num) {
newFile.copyFrom("\") ||\n\t ", 10);
} else {
newFile.copyFrom("\")) {\n", 6);
}
}
newFile.copyFrom(directResponse, strlen(directResponse));
}
void
AutoConf::addProxy(textBuffer & output, char *hostname, char *port, bool first, bool final)
{
if (first == false) {
// not first entry and more entries, JavaScript contcatenates
// strings with "+" operator
output.copyFrom(" + \n\t\t", 6);
}
output.copyFrom(proxyStr, strlen(proxyStr));
output.copyFrom(hostname, strlen(hostname));
output.copyFrom(":", 1);
output.copyFrom(port, strlen(port));
if (final == true) { // need to put ';' outside quote
output.copyFrom("\"; ", 3);
} else { // not final entry, so put ';' inside quote
output.copyFrom(";\" ", 3);
}
}
void
AutoConf::processAction(char *submission, textBuffer * output)
{
InkHashTable *vars = processFormSubmission(submission);
char *action = "Unknown";
PACresult r = PAC_OK;
bool genReply = true;
// Look up the necessary information from the form submission
if (!ink_hash_table_lookup(vars, "action", (void **) &action) || action == NULL) {
mgmt_log(stderr, "[AutoConf::processAction] Invalid Submission\n");
action = "Unknown";
r = PAC_INVALID_SUBMISSION;
}
if (r == PAC_OK) {
if (strcasecmp(action, "create") == 0) {
r = handleCreate(vars);
} else if (strcasecmp(action, "delete") == 0) {
r = handleRemove();
} else if (strcasecmp(action, "view") == 0) {
handleView(output, 0);
genReply = false;
} else if (strcasecmp(action, "abort") == 0) {
// r = PAC_OK; genReply = true;
// display the autoconf.html page
} else {
r = PAC_INVALID_SUBMISSION;
mgmt_log(stderr, "[AutoConf::processAction] Invalid Submission\n");
}
}
if (genReply == true) {
if (r == PAC_OK) {
this->displayAutoConfPage(output);
} else {
pacErrorResponse(action, r, output);
}
}
ink_hash_table_destroy_and_xfree_values(vars);
}
// void AutoConf::pacErrorResponse(char* action, PACresult error, textBuffer* output)
//
void
AutoConf::pacErrorResponse(char *action, PACresult error, textBuffer * output)
{
const char a[] =
"<html>\n<head>\n<title> Client AutoConfig Error </title>\n</head>\n<body bgcolor=\"#FFFFFF\">\n<h1> Client AutoConfig Error </h1>\n<p>\nClient AutoCnfig File ";
const char b[] = " failed: ";
const char c[] = "\n</p>\n<a href=\"/configure/autoconf.html\"> Continue</a>\n</body>\n</html>";
output->copyFrom(a, strlen(a));
output->copyFrom(action, strlen(action));
output->copyFrom(b, strlen(b));
output->copyFrom(pacStrings[error], strlen(pacStrings[error]));
output->copyFrom(c, strlen(c));
}
// Added extra argument 'flag' to distinguish between displaying
// the 'proxy.pac' info in a the frame or a seperate window. - GV
void
AutoConf::handleView(textBuffer * output, int flag // 0-> displaying in framset, 1-> displaying in seperate window
)
{
const char a[] =
"<html>\n<title> Configure: Current Auto Configuration File </title>\n</head>\n<body bgcolor=\"#FFFFFF\">\n<h1> Current Auto Configuration File </h1>\n<pre>\n";
const char a1[] =
"<html>\n<title> Configure: Auto Configuration File </title>\n</head>\n<body bgcolor=\"#FFFFFF\">\n<h1> Auto Configuration File</h1>\n <p> <b><em><font size=-1> Last modified: ";
const char b[] =
"\n</pre>\n<a href=\"/configure/autoconf.html\"> <img src=\"/images/back.gif\" border=\"0\"> Configure: Client Auto-Configuration </a>\n</body>\n</html>\n";
const char noBinding[] = "Internal Error Occured: No Binding to File";
const char readFailed[] = "Unable to retrieve file: ";
const char active1[] = "</font></em></b> <p>\n<pre>\n";
textBuffer *pac = NULL;
RollBackCodes r;
Rollback *pacRoll = NULL;
// following used to get modified times of 'proxy.pac'
bool pacFile = false;
struct stat fileInfo;
char dateBuf[64];
// prepare header of HTML response depending upon whether the data is
// being display in the frame or a seperate window
if (1 == flag) {
// seperate window
output->copyFrom(a1, strlen(a1));
if (configFiles->getRollbackObj("proxy.pac", &pacRoll) == false) {
output->copyFrom(active1, strlen(active1));
output->copyFrom(noBinding, strlen(noBinding));
} else {
r = pacRoll->getVersion(ACTIVE_VERSION, &pac);
// get modified time of 'proxy.pac' file
if (pacRoll->statVersion(ACTIVE_VERSION, &fileInfo) == 0) {
if (fileInfo.st_size > 0) {
pacFile = true;
}
}
char *result = ink_ctime_r(&fileInfo.st_mtime, dateBuf);
if (result != NULL) {
output->copyFrom(dateBuf, strlen(dateBuf));
} else {
output->copyFrom("???", 3);
}
output->copyFrom(active1, strlen(active1));
if (r == OK_ROLLBACK) {
output->copyFrom(pac->bufPtr(), pac->spaceUsed());
} else {
output->copyFrom(readFailed, strlen(readFailed));
}
}
} else {
// in frame
output->copyFrom(a, strlen(a));
if (configFiles->getRollbackObj("proxy.pac", &pacRoll) == false) {
output->copyFrom(noBinding, strlen(noBinding));
} else {
r = pacRoll->getVersion(ACTIVE_VERSION, &pac);
if (r == OK_ROLLBACK) {
output->copyFrom(pac->bufPtr(), pac->spaceUsed());
} else {
output->copyFrom(readFailed, strlen(readFailed));
}
}
}
if (0 == flag) // only display back button when displaying in frame
output->copyFrom(b, strlen(b));
delete pac;
}
PACresult
AutoConf::handleCreate(InkHashTable * params)
{
textBuffer newFile(2048);
Rollback *pacRoll;
if (BuildFile(params, newFile) == false) {
return PAC_INVALID_SUBMISSION;
}
if (configFiles->getRollbackObj("proxy.pac", &pacRoll) == false) {
return PAC_CREATE_FAILED;
}
if (pacRoll->forceUpdate(&newFile) != OK_ROLLBACK) {
return PAC_CREATE_FAILED;
}
return PAC_OK;
}
PACresult
AutoConf::handleRemove()
{
Rollback *pacRoll;
textBuffer empty(16);
if (configFiles->getRollbackObj("proxy.pac", &pacRoll) == false) {
return PAC_REMOVE_FAILED;
}
if (pacRoll->forceUpdate(&empty) != OK_ROLLBACK) {
return PAC_REMOVE_FAILED;
}
return PAC_OK;
}
// bool AutoConf::BuildFile(InkHashTable* parameters, textBuffer& newFile)
//
// Constructs a client autoconfig file into newFile from information
// contained in the parameters hashTable.
//
// Returns true if the construction was success and
// false otherwise
//
bool
AutoConf::BuildFile(InkHashTable * parameters, textBuffer & newFile)
{
char *val;
int num = 0;
ink_assert(parameters != NULL);
ExpandingArray clusterHosts(25, true);
char portBuf[20];
char rrNameBuf[64];
bool rrEnabled;
char *secondProxy = NULL;
char *secondPort = NULL;
bool clusterFO = false;
bool secondFO = false;
bool directFO = false;
int remainingFO = 0;
Tokenizer tok(" \t");
char *nodeFQHN;
bool FQHNfound;
newFile.copyFrom(fileHead, strlen(fileHead));
newFile.copyFrom(checkProtocol, strlen(checkProtocol));
// Handle hosts without Fully Qualified Domanin Names
if (ink_hash_table_lookup(parameters, "nq_hosts", (void **) &val)) {
newFile.copyFrom(checkNQ, strlen(checkNQ));
}
// Handle hosts to bypass because of domain name
if (ink_hash_table_lookup(parameters, "domain_bypass", (void **) &val)) {
if (val != NULL && *val != '\0') {
num = tok.Initialize(val, SHARE_TOKS);
this->byPass(newFile, tok, checkDomain);
}
}
// Handle hosts to bypass because of host name
if (ink_hash_table_lookup(parameters, "host_bypass", (void **) &val)) {
if (val != NULL && *val != '\0') {
num = tok.Initialize(val, SHARE_TOKS);
this->byPass(newFile, tok, checkHost);
}
}
// Generate the default case proxy string
//
// If virtual IP is enabled, use the round robin name, otherwise
// assume there is no round robin and just use this machine's
// hostname
//
if (lmgmt->virt_map->enabled == 0) {
rrEnabled = false;
if (varStrFromName("proxy.node.hostname_FQ", rrNameBuf, 64) == false) {
return false;
}
} else {
// Virtual IP
rrEnabled = true;
if (varStrFromName("proxy.config.proxy_name", rrNameBuf, 64) == false) {
return false;
}
}
if (varStrFromName("proxy.config.http.server_port", portBuf, 20) == false) {
return false;
}
// check for 'Internal Cluster Failover' option
if (ink_hash_table_lookup(parameters, "cluster_fo", (void **) &val)) {
// Get number of cluster nodes and enable option only if more than one node
num = overviewGenerator->getClusterHosts(&clusterHosts);
if (num > 1) {
clusterFO = true;
remainingFO++;
}
}
// check for 'Failover to Secondary Proxy' option
if (ink_hash_table_lookup(parameters, "second_fo", (void **) &val)) {
secondFO = true;
remainingFO++;
}
// check for 'Go to Direct as Last Resort' option
if (ink_hash_table_lookup(parameters, "direct_fo", (void **) &val)) {
directFO = true;
remainingFO++;
}
// Always add Round-Robin Name
newFile.copyFrom(returnStr, strlen(returnStr));
if (remainingFO)
this->addProxy(newFile, rrNameBuf, portBuf, true, false);
else
this->addProxy(newFile, rrNameBuf, portBuf, true, true);
if (clusterFO == true) {
// 'Internal Cluster Failover' option
remainingFO--;
int i;
// If the first response is a round robin name,
// include this machine in the cluster fail over
// list. If the first response is the hostname of
// this machine, skip over this machine in cluster
// fail over generation as not to repeat the name
// this proxy
if (rrEnabled == true) {
i = 0;
} else {
i = 1;
}
for (; i < num; i++) {
//coverity[alloc_fn]
//coverity[var_assign]
nodeFQHN = overviewGenerator->readString((char *) clusterHosts[i], "proxy.node.hostname_FQ", &FQHNfound);
// We should always be able to find a FQHN but if
// we don't just muddle through with the unqualified
// and hope for the best
if (FQHNfound == false) {
//coverity[overwrite_var]
nodeFQHN = xstrdup((char *) clusterHosts[i]);
}
if (remainingFO)
this->addProxy(newFile, nodeFQHN, portBuf, false, false);
else
this->addProxy(newFile, nodeFQHN, portBuf, false, true);
xfree(nodeFQHN);
}
}
if (secondFO == true) {
// 'Failover to Secondary Proxy' option
remainingFO--;
if (ink_hash_table_lookup(parameters, "second_proxy", (void **) &secondProxy) &&
ink_hash_table_lookup(parameters, "second_port", (void **) &secondPort)) {
if (secondProxy && secondPort && *secondProxy != '\0' && *secondPort != '\0') {
if (remainingFO)
this->addProxy(newFile, secondProxy, secondPort, false, false);
else
this->addProxy(newFile, secondProxy, secondPort, false, true);
}
}
}
if (directFO == true) {
// 'Go to Direct as Last Resort' option
remainingFO--;
newFile.copyFrom(" + \n\t\t", 6);
newFile.copyFrom(directStr, strlen(directStr));
}
newFile.copyFrom("\n}\n", 3);
return true;
}
void
please_give_me_debug_info()
{
}