blob: 65fb617e465423151fd8047741b1ca7448d9851e [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 "tscore/ink_platform.h"
#include "tscore/ink_args.h"
#include "tscore/I_Version.h"
#include "mgmtapi.h"
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string_view>
#include "tscore/Regex.h"
/// XXX Use DFA or Regex wrappers?
#ifdef HAVE_PCRE_PCRE_H
#include <pcre/pcre.h>
#else
#include <pcre.h>
#endif
#define SUBSTRING_VECTOR_COUNT 30 // Should be multiple of 3
static AppVersionInfo appVersionInfo;
struct VIA {
VIA(const char *t) : title(t) {}
~VIA() { delete next; }
const char *title;
const char *viaData[128] = {}; // zero initialize
VIA *next = nullptr;
};
// Function to get via header table for every field/category in the via header
static VIA *
detailViaLookup(char flag)
{
VIA *viaTable;
// Detailed via codes after ":"
switch (flag) {
case 't':
viaTable = new VIA("Tunnel info");
viaTable->viaData[static_cast<unsigned char>(' ')] = "no tunneling";
viaTable->viaData[static_cast<unsigned char>('U')] = "tunneling because of url (url suggests dynamic content)";
viaTable->viaData[static_cast<unsigned char>('M')] = "tunneling due to a method (e.g. CONNECT)";
viaTable->viaData[static_cast<unsigned char>('O')] = "tunneling because cache is turned off";
viaTable->viaData[static_cast<unsigned char>('F')] = "tunneling due to a header field (such as presence of If-Range header)";
viaTable->viaData[static_cast<unsigned char>('N')] = "tunneling due to no forward";
viaTable->viaData[static_cast<unsigned char>('A')] = "tunnel authorization";
break;
case 'c':
// Cache type
viaTable = new VIA("Cache Type");
viaTable->viaData[static_cast<unsigned char>('C')] = "cache";
viaTable->viaData[static_cast<unsigned char>('L')] = "cluster, (not used)";
viaTable->viaData[static_cast<unsigned char>('P')] = "parent";
viaTable->viaData[static_cast<unsigned char>('S')] = "server";
viaTable->viaData[static_cast<unsigned char>(' ')] = "unknown";
// Cache Lookup Result
viaTable->next = new VIA("Cache Lookup Result");
viaTable->next->viaData[static_cast<unsigned char>('C')] = "cache hit but config forces revalidate";
viaTable->next->viaData[static_cast<unsigned char>('I')] =
"conditional miss (client sent conditional, fresh in cache, returned 412)";
viaTable->next->viaData[static_cast<unsigned char>(' ')] = "cache miss or no cache lookup";
viaTable->next->viaData[static_cast<unsigned char>('U')] = "cache hit, but client forces revalidate (e.g. Pragma: no-cache)";
viaTable->next->viaData[static_cast<unsigned char>('D')] = "cache hit, but method forces revalidated (e.g. ftp, not anonymous)";
viaTable->next->viaData[static_cast<unsigned char>('M')] = "cache miss (url not in cache)";
viaTable->next->viaData[static_cast<unsigned char>('N')] =
"conditional hit (client sent conditional, doc fresh in cache, returned 304)";
viaTable->next->viaData[static_cast<unsigned char>('H')] = "cache hit";
viaTable->next->viaData[static_cast<unsigned char>('S')] = "cache hit, but expired";
viaTable->next->viaData[static_cast<unsigned char>('K')] = "cookie miss";
break;
case 'p':
viaTable = new VIA("Parent proxy connection status");
viaTable->viaData[static_cast<unsigned char>(' ')] = "no parent proxy or unknown";
viaTable->viaData[static_cast<unsigned char>('S')] = "connection opened successfully";
viaTable->viaData[static_cast<unsigned char>('F')] = "connection open failed";
break;
case 's':
viaTable = new VIA("Origin server connection status");
viaTable->viaData[static_cast<unsigned char>(' ')] = "no server connection needed";
viaTable->viaData[static_cast<unsigned char>('S')] = "connection opened successfully";
viaTable->viaData[static_cast<unsigned char>('F')] = "connection open failed";
break;
default:
viaTable = nullptr;
fprintf(stderr, "%s: %s: %c\n", appVersionInfo.AppStr, "Invalid VIA header character", flag);
break;
}
return viaTable;
}
// Function to get via header table for every field/category in the via header
static VIA *
standardViaLookup(char flag)
{
VIA *viaTable;
// Via codes before ":"
switch (flag) {
case 'u':
viaTable = new VIA("Request headers received from client");
viaTable->viaData[static_cast<unsigned char>('C')] = "cookie";
viaTable->viaData[static_cast<unsigned char>('E')] = "error in request";
viaTable->viaData[static_cast<unsigned char>('S')] = "simple request (not conditional)";
viaTable->viaData[static_cast<unsigned char>('N')] = "no-cache";
viaTable->viaData[static_cast<unsigned char>('I')] = "IMS";
viaTable->viaData[static_cast<unsigned char>(' ')] = "unknown";
break;
case 'c':
viaTable = new VIA("Result of Traffic Server cache lookup for URL");
viaTable->viaData[static_cast<unsigned char>('A')] = "in cache, not acceptable (a cache \"MISS\")";
viaTable->viaData[static_cast<unsigned char>('H')] = "in cache, fresh (a cache \"HIT\")";
viaTable->viaData[static_cast<unsigned char>('S')] = "in cache, stale (a cache \"MISS\")";
viaTable->viaData[static_cast<unsigned char>('R')] = "in cache, fresh Ram hit (a cache \"HIT\")";
viaTable->viaData[static_cast<unsigned char>('M')] = "miss (a cache \"MISS\")";
viaTable->viaData[static_cast<unsigned char>(' ')] = "no cache lookup";
break;
case 's':
viaTable = new VIA("Response information received from origin server");
viaTable->viaData[static_cast<unsigned char>('E')] = "error in response";
viaTable->viaData[static_cast<unsigned char>('S')] = "connection opened successfully";
viaTable->viaData[static_cast<unsigned char>('N')] = "not-modified";
viaTable->viaData[static_cast<unsigned char>(' ')] = "no server connection needed";
break;
case 'f':
viaTable = new VIA("Result of document write-to-cache:");
viaTable->viaData[static_cast<unsigned char>('U')] = "updated old cache copy";
viaTable->viaData[static_cast<unsigned char>('D')] = "cached copy deleted";
viaTable->viaData[static_cast<unsigned char>('W')] = "written into cache (new copy)";
viaTable->viaData[static_cast<unsigned char>(' ')] = "no cache write performed";
break;
case 'p':
viaTable = new VIA("Proxy operation result");
viaTable->viaData[static_cast<unsigned char>('R')] = "origin server revalidated";
viaTable->viaData[static_cast<unsigned char>(' ')] = "unknown";
viaTable->viaData[static_cast<unsigned char>('S')] = "served or connection opened successfully";
viaTable->viaData[static_cast<unsigned char>('N')] = "not-modified";
break;
case 'e':
viaTable = new VIA("Error codes (if any)");
viaTable->viaData[static_cast<unsigned char>('A')] = "authorization failure";
viaTable->viaData[static_cast<unsigned char>('H')] = "header syntax unacceptable";
viaTable->viaData[static_cast<unsigned char>('C')] = "connection to server failed";
viaTable->viaData[static_cast<unsigned char>('T')] = "connection timed out";
viaTable->viaData[static_cast<unsigned char>('S')] = "server related error";
viaTable->viaData[static_cast<unsigned char>('D')] = "dns failure";
viaTable->viaData[static_cast<unsigned char>('N')] = "no error";
viaTable->viaData[static_cast<unsigned char>('F')] = "request forbidden";
viaTable->viaData[static_cast<unsigned char>('R')] = "cache read error";
viaTable->viaData[static_cast<unsigned char>('M')] = "moved temporarily";
viaTable->viaData[static_cast<unsigned char>('L')] = "looped detected";
viaTable->viaData[static_cast<unsigned char>(' ')] = "unknown";
break;
default:
viaTable = nullptr;
fprintf(stderr, "%s: %s: %c\n", appVersionInfo.AppStr, "Invalid VIA header character", flag);
break;
}
return viaTable;
}
// Function to print via header
static void
printViaHeader(std::string_view header)
{
VIA *viaTable = nullptr;
VIA *viaEntry = nullptr;
bool isDetail = false;
printf("Via Header Details:\n");
// Loop through input via header flags
for (char c : header) {
if ((c == ':') || (c == ';')) {
isDetail = true;
continue;
}
if (islower(c)) {
// Get the via header table
delete viaTable;
viaEntry = viaTable = isDetail ? detailViaLookup(c) : standardViaLookup(c);
} else {
// This is a one of the sequence of (uppercase) VIA codes.
if (viaEntry) {
unsigned char idx = c;
printf("%-55s:", viaEntry->title);
printf("%s\n", viaEntry->viaData[idx] ? viaEntry->viaData[idx] : "Invalid sequence");
viaEntry = viaEntry->next;
}
}
}
delete viaTable;
}
// Check validity of via header and then decode it
static TSMgmtError
decodeViaHeader(std::string_view text)
{
// Via header inside square brackets
if (!text.empty() && text.front() == '[' && text.back() == ']') {
text.remove_prefix(1);
text.remove_suffix(1);
}
if (text.empty()) {
return TS_ERR_FAIL;
}
printf("Via header is [%.*s], Length is %zu\n", int(text.size()), text.data(), text.size());
char extender[6];
if (text.size() == 5) { // add a trailing space in this case.
memcpy(extender, text.data(), text.size());
extender[5] = ' ';
text = std::string_view{extender, 6};
}
if (text.size() == 22 || text.size() == 6) {
// Decode via header
printViaHeader(text);
return TS_ERR_OKAY;
}
// Invalid header size, come out.
printf("\nInvalid VIA header. VIA header length should be 6 or 22 characters\n");
printf("Valid via header format is "
"[u<client-stuff>c<cache-lookup-stuff>s<server-stuff>f<cache-fill-stuff>p<proxy-stuff>e<error-codes>:t<tunneling-info>c<"
"cache type><cache-lookup-result>p<parent-proxy-conn-info>s<server-conn-info>]\n");
return TS_ERR_FAIL;
}
// Read user input from stdin
static TSMgmtError
filterViaHeader()
{
const pcre *compiledReg;
const pcre_extra *extraReg = nullptr;
int subStringVector[SUBSTRING_VECTOR_COUNT];
const char *err;
int errOffset;
int pcreExecCode;
int i;
const char *viaPattern =
R"(\[([ucsfpe]+[^\]]+)\])"; // Regex to match via header with in [] which can start with character class ucsfpe
std::string line;
// Compile PCRE via header pattern
compiledReg = pcre_compile(viaPattern, 0, &err, &errOffset, nullptr);
if (compiledReg == nullptr) {
printf("PCRE regex compilation failed with error %s at offset %d\n", err, errOffset);
return TS_ERR_FAIL;
}
// Read all lines from stdin
while (std::getline(std::cin, line)) {
// Match for via header pattern
pcreExecCode = pcre_exec(compiledReg, extraReg, line.data(), line.size(), 0, 0, subStringVector, SUBSTRING_VECTOR_COUNT);
// Match failed, don't worry. Continue to next line.
if (pcreExecCode < 0) {
continue;
}
// Match successful, but too many substrings
if (pcreExecCode == 0) {
pcreExecCode = SUBSTRING_VECTOR_COUNT / 3;
printf("Too many substrings were found. %d substrings couldn't fit into subStringVector\n", pcreExecCode - 1);
}
// Loop based on number of matches found
for (i = 1; i < pcreExecCode; i++) {
std::string_view match{line.data() + subStringVector[2 * i], size_t(subStringVector[2 * i + 1] - subStringVector[2 * i])};
// Decode matched substring
decodeViaHeader(match);
}
}
return TS_ERR_OKAY;
}
int
main(int /* argc ATS_UNUSED */, const char **argv)
{
TSMgmtError status;
// build the application information structure
appVersionInfo.setup(PACKAGE_NAME, "traffic_via", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
/* see 'ink_args.h' for meanings of the various fields */
ArgumentDescription argument_descriptions[] = {
VERSION_ARGUMENT_DESCRIPTION(),
HELP_ARGUMENT_DESCRIPTION(),
};
process_args(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv);
for (unsigned i = 0; i < n_file_arguments; ++i) {
if (strcmp(file_arguments[i], "-") == 0) {
// Filter arguments provided from stdin
status = filterViaHeader();
} else {
status = decodeViaHeader(std::string_view{file_arguments[i], strlen(file_arguments[i])});
}
if (status != TS_ERR_OKAY) {
return 1;
}
}
return 0;
}