/* * Copyright (C) 1996-2017 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #include "squid.h" #include "format/Config.h" #include "format/Token.h" #include "format/TokenTableEntry.h" #include "globals.h" #include "SquidConfig.h" #include "Store.h" // Due to token overlaps between 1 and 2 letter tokens (Bug 3310) // We split the token table into sets determined by the token length namespace Format { /// 1-char tokens. static TokenTableEntry TokenTable1C[] = { {">a", LFT_CLIENT_IP_ADDRESS}, {">p", LFT_CLIENT_PORT}, {">A", LFT_CLIENT_FQDN}, {"h", LFT_REQUEST_HEADER}, {">h", LFT_REQUEST_ALL_HEADERS}, {"v", LFT_REQUEST_VERSION_OLD_2X}, {"%", LFT_PERCENT}, {NULL, LFT_NONE} /* this must be last */ }; /// 2-char tokens static TokenTableEntry TokenTable2C[] = { {">la", LFT_CLIENT_LOCAL_IP}, {"la", LFT_LOCAL_LISTENING_IP}, {">lp", LFT_CLIENT_LOCAL_PORT}, {"lp", LFT_LOCAL_LISTENING_PORT}, /*{ "lA", LFT_LOCAL_NAME }, */ {"ha", LFT_ADAPTED_REQUEST_HEADER}, {">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS}, {"un", LFT_USER_NAME}, {"ul", LFT_USER_LOGIN}, /*{ "ur", LFT_USER_REALM }, */ /*{ "us", LFT_USER_SCHEME }, */ {"ui", LFT_USER_IDENT}, {"ue", LFT_USER_EXTERNAL}, {"Hs", LFT_HTTP_SENT_STATUS_CODE_OLD_30}, {">Hs", LFT_HTTP_SENT_STATUS_CODE}, {"rm", LFT_CLIENT_REQ_METHOD}, {">ru", LFT_CLIENT_REQ_URI}, {">rs", LFT_CLIENT_REQ_URLSCHEME}, {">rd", LFT_CLIENT_REQ_URLDOMAIN}, {">rP", LFT_CLIENT_REQ_URLPORT}, {">rp", LFT_CLIENT_REQ_URLPATH}, /*{">rq", LFT_CLIENT_REQ_QUERY},*/ {">rv", LFT_CLIENT_REQ_VERSION}, {"rm", LFT_REQUEST_METHOD}, {"ru", LFT_REQUEST_URI}, /* doesn't include the query-string */ {"rp", LFT_REQUEST_URLPATH_OLD_31}, /* { "rq", LFT_REQUEST_QUERY }, * / / * the query-string, INCLUDING the leading ? */ {"rv", LFT_REQUEST_VERSION}, {"rG", LFT_REQUEST_URLGROUP_OLD_2X}, {"st", LFT_CLIENT_REQUEST_SIZE_TOTAL }, {">sh", LFT_CLIENT_REQUEST_SIZE_HEADERS }, /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */ /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */ {"2 byte tokens static TokenTableEntry TokenTableMisc[] = { {">eui", LFT_CLIENT_EUI}, {">qos", LFT_CLIENT_LOCAL_TOS}, {"nfmark", LFT_CLIENT_LOCAL_NFMARK}, {"st", LFT_ICAP_BYTES_SENT}, {"h", LFT_ICAP_REQ_HEADER}, {"cert_subject", LFT_SSL_USER_CERT_SUBJECT}, {">cert_issuer", LFT_SSL_USER_CERT_ISSUER}, {">sni", LFT_SSL_CLIENT_SNI}, /*{"configTag != NULL; ++lte) { debugs(46, 8, HERE << "compare tokens '" << lte->configTag << "' with '" << cur << "'"); if (strncmp(lte->configTag, cur, strlen(lte->configTag)) == 0) { type = lte->tokenType; label = lte->configTag; debugs(46, 7, HERE << "Found token '" << label << "'"); return cur + strlen(lte->configTag); } } return cur; } /* parses a single token. Returns the token length in characters, * and fills in the lt item with the token information. * def is for sure null-terminated */ int Format::Token::parse(const char *def, Quoting *quoting) { const char *cur = def; int l; l = strcspn(cur, "%"); if (l > 0) { char *cp; /* it's a string for sure, until \0 or the next % */ cp = (char *)xmalloc(l + 1); xstrncpy(cp, cur, l + 1); type = LFT_STRING; data.string = cp; while (l > 0) { switch (*cur) { case '"': if (*quoting == LOG_QUOTE_NONE) *quoting = LOG_QUOTE_QUOTES; else if (*quoting == LOG_QUOTE_QUOTES) *quoting = LOG_QUOTE_NONE; break; case '[': if (*quoting == LOG_QUOTE_NONE) *quoting = LOG_QUOTE_MIMEBLOB; break; case ']': if (*quoting == LOG_QUOTE_MIMEBLOB) *quoting = LOG_QUOTE_NONE; break; } ++cur; --l; } } else if (*cur) { ++cur; // select quoting style for his particular token switch (*cur) { case '"': quote = LOG_QUOTE_QUOTES; ++cur; break; case '\'': quote = LOG_QUOTE_RAW; ++cur; break; case '[': quote = LOG_QUOTE_MIMEBLOB; ++cur; break; case '#': quote = LOG_QUOTE_URL; ++cur; break; default: quote = *quoting; break; } if (*cur == '-') { left = true; ++cur; } if (*cur == '0') { zero = true; ++cur; } char *endp; if (xisdigit(*cur)) { widthMin = strtol(cur, &endp, 10); cur = endp; } if (*cur == '.' && xisdigit(*(++cur))) { widthMax = strtol(cur, &endp, 10); cur = endp; } if (*cur == '{') { char *cp; ++cur; l = strcspn(cur, "}"); cp = (char *)xmalloc(l + 1); xstrncpy(cp, cur, l + 1); data.string = cp; cur += l; if (*cur == '}') ++cur; } type = LFT_NONE; // Scan each registered token namespace debugs(46, 9, HERE << "check for token in " << TheConfig.tokens.size() << " namespaces."); for (std::list::const_iterator itr = TheConfig.tokens.begin(); itr != TheConfig.tokens.end(); ++itr) { debugs(46, 7, HERE << "check for possible " << itr->prefix << ":: token"); const size_t len = itr->prefix.size(); if (itr->prefix.cmp(cur, len) == 0 && cur[len] == ':' && cur[len+1] == ':') { debugs(46, 5, HERE << "check for " << itr->prefix << ":: token in '" << cur << "'"); const char *old = cur; cur = scanForToken(itr->tokenSet, cur+len+2); if (old != cur) // found break; else // reset to start of namespace cur = cur - len - 2; } } if (type == LFT_NONE) { // For upward compatibility, assume "http::" prefix as default prefix // for all log access formatting codes, except those starting with a // "%" or a known namespace. (ie "icap::", "adapt::") if (strncmp(cur,"http::", 6) == 0 && *(cur+6) != '%' ) cur += 6; // NP: scan the sets of tokens in decreasing size to guarantee no // mistakes made with overlapping names. (Bug 3310) // Scan for various long tokens debugs(46, 5, HERE << "scan for possible Misc token"); cur = scanForToken(TokenTableMisc, cur); // scan for 2-char tokens if (type == LFT_NONE) { debugs(46, 5, HERE << "scan for possible 2C token"); cur = scanForToken(TokenTable2C, cur); } // finally scan for 1-char tokens. if (type == LFT_NONE) { debugs(46, 5, HERE << "scan for possible 1C token"); cur = scanForToken(TokenTable1C, cur); } } if (type == LFT_NONE) { fatalf("Can't parse configuration token: '%s'\n", def); } if (*cur == ' ') { space = true; ++cur; } } switch (type) { #if USE_ADAPTATION case LFT_ADAPTATION_LAST_HEADER: #endif #if ICAP_CLIENT case LFT_ICAP_REQ_HEADER: case LFT_ICAP_REP_HEADER: #endif case LFT_ADAPTED_REQUEST_HEADER: case LFT_REQUEST_HEADER: case LFT_REPLY_HEADER: case LFT_NOTE: if (data.string) { char *header = data.string; char *cp = strchr(header, ':'); if (cp) { *cp = '\0'; ++cp; if (*cp == ',' || *cp == ';' || *cp == ':') { data.header.separator = *cp; ++cp; } else { data.header.separator = ','; } data.header.element = cp; switch (type) { case LFT_REQUEST_HEADER: type = LFT_REQUEST_HEADER_ELEM; break; case LFT_ADAPTED_REQUEST_HEADER: type = LFT_ADAPTED_REQUEST_HEADER_ELEM; break; case LFT_REPLY_HEADER: type = LFT_REPLY_HEADER_ELEM; break; #if USE_ADAPTATION case LFT_ADAPTATION_LAST_HEADER: type = LFT_ADAPTATION_LAST_HEADER_ELEM; break; #endif #if ICAP_CLIENT case LFT_ICAP_REQ_HEADER: type = LFT_ICAP_REQ_HEADER_ELEM; break; case LFT_ICAP_REP_HEADER: type = LFT_ICAP_REP_HEADER_ELEM; break; #endif default: break; } } data.header.header = header; } else { switch (type) { case LFT_REQUEST_HEADER: type = LFT_REQUEST_ALL_HEADERS; break; case LFT_ADAPTED_REQUEST_HEADER: type = LFT_ADAPTED_REQUEST_ALL_HEADERS; break; case LFT_REPLY_HEADER: type = LFT_REPLY_ALL_HEADERS; break; #if USE_ADAPTATION case LFT_ADAPTATION_LAST_HEADER: type = LFT_ADAPTATION_LAST_ALL_HEADERS; break; #endif #if ICAP_CLIENT case LFT_ICAP_REQ_HEADER: type = LFT_ICAP_REQ_ALL_HEADERS; break; case LFT_ICAP_REP_HEADER: type = LFT_ICAP_REP_ALL_HEADERS; break; #endif default: break; } Config.onoff.log_mime_hdrs = 1; } break; case LFT_CLIENT_FQDN: Config.onoff.log_fqdn = 1; break; case LFT_TIME_START: case LFT_TIME_SUBSECOND: divisor = 1000; if (widthMax > 0) { divisor = 1000000; for (int i = widthMax; i > 0; --i) divisor /= 10; if (!divisor) divisor = 1; } break; case LFT_HTTP_SENT_STATUS_CODE_OLD_30: debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"Hs\" formatting code is deprecated. Use the \">Hs\" instead."); type = LFT_HTTP_SENT_STATUS_CODE; break; case LFT_SERVER_LOCAL_IP_OLD_27: debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"oa\" formatting code is deprecated. Use the \"rp\" instead."); type = LFT_CLIENT_REQ_URLPATH; break; case LFT_REQUEST_VERSION_OLD_2X: debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \">v\" formatting code is deprecated. Use the \">rv\" instead."); type = LFT_REQUEST_VERSION; break; #if !USE_SQUID_EUI case LFT_CLIENT_EUI: debugs(46, DBG_CRITICAL, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid."); break; #endif case LFT_REQUEST_URLGROUP_OLD_2X: debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rG\" formatting code is deprecated. Use \"note{urlgroup}\" instead."); type = LFT_NOTE; data.header.header = xstrdup("urlgroup"); break; default: break; } return (cur - def); } Format::Token::Token() : type(LFT_NONE), label(NULL), widthMin(-1), widthMax(-1), quote(LOG_QUOTE_NONE), left(false), space(false), zero(false), divisor(1), next(NULL) { data.string = NULL; data.header.header = NULL; data.header.element = NULL; data.header.separator = ','; } Format::Token::~Token() { label = NULL; // drop reference to global static. safe_free(data.string); while (next) { Token *tokens = next; next = next->next; tokens->next = NULL; delete tokens; } }