]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Convert external_acl_type directive format tokens
authorAmos Jeffries <squid3@treenet.co.nz>
Fri, 29 May 2015 13:50:26 +0000 (06:50 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 29 May 2015 13:50:26 +0000 (06:50 -0700)
Use libformat tokenizer and string assembler mechanisms instead of
custom code for external helpers. This allows any logformat code and
transaction data to be used by the external ACL helper.

The old helper format tokens are added to the libformat for backward
compatibility.

Known issue:
  all access lists which use an exetrnal_acl_type helper now require
  setting up the ACL Checklist with an AccessLogEntry referencing any
  special token data the old format tokens needed.

12 files changed:
src/AccessLogEntry.h
src/ExternalACL.h
src/Makefile.am
src/acl/Acl.cc
src/acl/Acl.h
src/acl/Checklist.h
src/acl/FilledChecklist.h
src/cf.data.pre
src/external_acl.cc
src/format/Format.cc
src/format/Format.h
src/format/Token.cc

index 24a59ac9a01597096ea9a9b3233f24f18b71f8b4..8731a0ea0bf10d3c050d24d859c4873ef675c551 100644 (file)
@@ -209,9 +209,16 @@ public:
     {
 
     public:
-        Private() : method_str(NULL) {}
+        Private() : method_str(NULL), lastAclName(NULL), lastAclData(NULL) {}
+        ~Private() {
+            safe_free(lastAclName);
+            safe_free(lastAclData);
+        }
 
         const char *method_str;
+        const char *lastAclName;
+        const char *lastAclData;
+
     } _private;
     HierarchyLogEntry hier;
     HttpReply *reply;
index bea24e3c7bf5c0f296dac626be5490d778ae1420..1299c017ba58c1870971072f81a2d3265ccf3d52 100644 (file)
@@ -51,6 +51,7 @@ public:
     virtual void parse();
     virtual int match(ACLChecklist *checklist);
     /* This really should be dynamic based on the external class defn */
+    virtual bool requiresAleXXX() const {return true;}
     virtual bool requiresRequest() const {return true;}
 
     /* when requiresRequest is made dynamic, review this too */
index b8d1ea2acb1c604a1a37523532e1aea13225c9eb..90bc10e2add9c3231acc69e5f4e2c86ce96f447a 100644 (file)
@@ -3524,6 +3524,7 @@ tests_testURL_SOURCES = \
        int.cc \
        internal.h \
        internal.cc \
+       tests/stub_libeui.cc \
        tests/stub_libsecurity.cc \
        SquidList.h \
        SquidList.cc \
index dcbc5208896347aae6ad08f690fe6a183b0583ee..b81faeabc2d054e8c4e5c397944a29198ae3af39 100644 (file)
@@ -144,7 +144,10 @@ ACL::matches(ACLChecklist *checklist) const
     AclMatchedName = name;
 
     int result = 0;
-    if (!checklist->hasRequest() && requiresRequest()) {
+    if (!checklist->hasAleXXX() && requiresAleXXX()) {
+        debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
+               "context without an HTTP request. Assuming mismatch.");
+    } else if (!checklist->hasRequest() && requiresRequest()) {
         debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
                "context without an HTTP request. Assuming mismatch.");
     } else if (!checklist->hasReply() && requiresReply()) {
@@ -360,6 +363,12 @@ aclCacheMatchFlush(dlink_list * cache)
     }
 }
 
+bool
+ACL::requiresAleXXX() const
+{
+    return false;
+}
+
 bool
 ACL::requiresReply() const
 {
index 70831062bc9466091232f6ccea262d915dbdb1f1..3ad9abb83cbacabf5e6aec02276047eb4e77667d 100644 (file)
@@ -143,6 +143,8 @@ private:
     /// Matches the actual data in checklist against this ACL.
     virtual int match(ACLChecklist *checklist) = 0; // XXX: missing const
 
+    /// whether our (i.e. shallow) match() requires checklist to have a AccessLogEntry
+    virtual bool requiresAleXXX() const;
     /// whether our (i.e. shallow) match() requires checklist to have a request
     virtual bool requiresRequest() const;
     /// whether our (i.e. shallow) match() requires checklist to have a reply
index 4d6afbaacf21687c9728d81dccc3e23ba9b596c8..fd1413f521afa82b8d094fd6b5e3222df205cfab 100644 (file)
@@ -157,6 +157,7 @@ public:
     // for ACL::checklistMatches to use
     virtual bool hasRequest() const = 0;
     virtual bool hasReply() const = 0;
+    virtual bool hasAleXXX() const = 0;
 
 private:
     /// Calls non-blocking check callback with the answer and destroys self.
index 641272513e246c30cb0bb6ed10937591f7e6f0e0..67a566f80aa2a9f4bf53d90a2f0426dc736b8aa5 100644 (file)
@@ -62,6 +62,7 @@ public:
     // ACLChecklist API
     virtual bool hasRequest() const { return request != NULL; }
     virtual bool hasReply() const { return reply != NULL; }
+    virtual bool hasAleXXX() const { return al != NULL; }
 
 public:
     Ip::Address src_addr;
@@ -88,7 +89,7 @@ public:
     Ssl::X509_Pointer serverCert;
 #endif
 
-    AccessLogEntry::Pointer al; ///< info for the future access.log entry
+    AccessLogEntry::Pointer al; ///< info for the future access.log, and external ACL
 
     ExternalACLEntryPointer extacl_entry;
 
index 6975ca293d2f2caf26d745f4531839436c0aaa7d..ed7ca8616d9af1860a91049fdb018c70c00803aa 100644 (file)
@@ -4059,7 +4059,7 @@ DOC_START
        modifiers are usually not needed, but can be specified if an explicit
        output format is desired.
 
-               % ["|[|'|#] [-] [[0]width] [{argument}] formatcode
+               % ["|[|'|#] [-] [[0]width] [{arg}] formatcode [{arg}]
 
                "       output in quoted string format
                [       output in squid text log format as used by log_mime_hdrs
@@ -4073,7 +4073,8 @@ DOC_START
                        When minimum starts with 0, the field is zero-padded.
                        String values exceeding maximum width are truncated.
 
-               {arg}   argument such as header name etc
+               {arg}   argument such as header name etc. This field may be
+                       placed before or after the token, but not both at once.
 
        Format codes:
 
index 73fa16c08a193173092db14f27e6dfdfca8a0c0b..c5bec244d18343b17c9a5206bdf6d1bc74927baa 100644 (file)
 #include "acl/FilledChecklist.h"
 #include "cache_cf.h"
 #include "client_side.h"
+#include "client_side_request.h"
 #include "comm/Connection.h"
 #include "ConfigParser.h"
 #include "ExternalACL.h"
 #include "ExternalACLEntry.h"
 #include "fde.h"
-#include "format/ByteCode.h"
+#include "format/Token.h"
 #include "helper.h"
 #include "helper/Reply.h"
 #include "HttpHeaderTools.h"
@@ -66,27 +67,6 @@ static ExternalACLEntryPointer external_acl_cache_add(external_acl * def, const
  * external_acl directive
  */
 
-class external_acl_format : public RefCountable
-{
-    MEMPROXY_CLASS(external_acl_format);
-
-public:
-    typedef RefCount<external_acl_format> Pointer;
-
-    external_acl_format() : type(Format::LFT_NONE), header(NULL), member(NULL), separator(' '), header_id(HDR_BAD_HDR) {}
-    ~external_acl_format() {
-        xfree(header);
-        xfree(member);
-    }
-
-    Format::ByteCode_t type;
-    external_acl_format::Pointer next;
-    char *header;
-    char *member;
-    char separator;
-    http_hdr_type header_id;
-};
-
 class external_acl
 {
     /* FIXME: These are not really cbdata, but it is an easy way
@@ -112,7 +92,7 @@ public:
 
     char *name;
 
-    external_acl_format::Pointer format;
+    Format::Format format;
 
     wordlist *cmdline;
 
@@ -156,6 +136,7 @@ external_acl::external_acl() :
     negative_ttl(-1),
     grace(1),
     name(NULL),
+    format("external_acl_type"),
     cmdline(NULL),
     children(DEFAULT_EXTERNAL_ACL_CHILDREN),
     theHelper(NULL),
@@ -173,7 +154,6 @@ external_acl::external_acl() :
 external_acl::~external_acl()
 {
     xfree(name);
-    format = NULL;
     wordlistDestroy(&cmdline);
 
     if (theHelper) {
@@ -197,56 +177,6 @@ external_acl::~external_acl()
     }
 }
 
-/**
- * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
- * request or reply header to external helper.
- *
- \param header   - the token being parsed (without the identifying prefix)
- \param type     - format enum identifier for this element, pulled from identifying prefix
- \param format   - structure to contain all the info about this format element.
- */
-void
-parse_header_token(external_acl_format::Pointer format, char *header, const Format::ByteCode_t type)
-{
-    /* header format */
-    char *member, *end;
-
-    /** Cut away the closing brace */
-    end = strchr(header, '}');
-    if (end && strlen(end) == 1)
-        *end = '\0';
-    else
-        self_destruct();
-
-    member = strchr(header, ':');
-
-    if (member) {
-        /* Split in header and member */
-        *member = '\0';
-        ++member;
-
-        if (!xisalnum(*member)) {
-            format->separator = *member;
-            ++member;
-        } else {
-            format->separator = ',';
-        }
-
-        format->member = xstrdup(member);
-
-        if (type == Format::LFT_ADAPTED_REQUEST_HEADER)
-            format->type = Format::LFT_ADAPTED_REQUEST_HEADER_ELEM;
-        else
-            format->type = Format::LFT_REPLY_HEADER_ELEM;
-
-    } else {
-        format->type = type;
-    }
-
-    format->header = xstrdup(header);
-    format->header_id = httpHeaderIdByNameDef(header, strlen(header));
-}
-
 void
 parse_externalAclHelper(external_acl ** list)
 {
@@ -332,115 +262,56 @@ parse_externalAclHelper(external_acl ** list)
     if (a->children.defaultQueueSize)
         a->children.queue_size = 2 * a->children.n_max;
 
-    /* Parse format */
-    external_acl_format::Pointer *p = &a->format;
-
+    /* Legacy external_acl_type format parser.
+     * Handles a series of %... tokens where any non-% means
+     * the start of another parameter field (ie the path to binary).
+     */
+    enum Format::Quoting quote = Format::LOG_QUOTE_NONE;
+    Format::Token **fmt = &a->format.format;
     while (token) {
-        /* stop on first non-format token found */
-
+        /* stop on first non-% token found */
         if (*token != '%')
             break;
 
-        external_acl_format::Pointer format = new external_acl_format;
-
-        if (strncmp(token, "%{", 2) == 0) {
-            // deprecated. but assume the old configs all referred to request headers.
-            debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token);
-            parse_header_token(format, (token+2), Format::LFT_ADAPTED_REQUEST_HEADER);
-        } else if (strncmp(token, "%>{", 3) == 0) {
-            debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token);
-            parse_header_token(format, (token+3), Format::LFT_ADAPTED_REQUEST_HEADER);
-        } else if (strncmp(token, "%>ha{", 5) == 0) {
-            parse_header_token(format, (token+5), Format::LFT_ADAPTED_REQUEST_HEADER);
-        } else if (strncmp(token, "%<{", 3) == 0) {
-            debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %<{...} is being replaced by %<h{...} for : " << token);
-            parse_header_token(format, (token+3), Format::LFT_REPLY_HEADER);
-        } else if (strncmp(token, "%<h{", 4) == 0) {
-            parse_header_token(format, (token+4), Format::LFT_REPLY_HEADER);
-#if USE_AUTH
-        } else if (strcmp(token, "%LOGIN") == 0 || strcmp(token, "%ul") == 0) {
-            format->type = Format::LFT_USER_LOGIN;
-            a->require_auth = true;
-#endif
-        }
-#if USE_IDENT
-        else if (strcmp(token, "%IDENT") == 0 || strcmp(token, "%ui") == 0)
-            format->type = Format::LFT_USER_IDENT;
-#endif
-        else if (strcmp(token, "%SRC") == 0 || strcmp(token, "%>a") == 0)
-            format->type = Format::LFT_CLIENT_IP_ADDRESS;
-        else if (strcmp(token, "%SRCPORT") == 0 || strcmp(token, "%>p") == 0)
-            format->type = Format::LFT_CLIENT_PORT;
-#if USE_SQUID_EUI
-        else if (strcmp(token, "%SRCEUI48") == 0)
-            format->type = Format::LFT_EXT_ACL_CLIENT_EUI48;
-        else if (strcmp(token, "%SRCEUI64") == 0)
-            format->type = Format::LFT_EXT_ACL_CLIENT_EUI64;
-#endif
-        else if (strcmp(token, "%MYADDR") == 0 || strcmp(token, "%la") == 0)
-            format->type = Format::LFT_LOCAL_LISTENING_IP;
-        else if (strcmp(token, "%MYPORT") == 0 || strcmp(token, "%lp") == 0)
-            format->type = Format::LFT_LOCAL_LISTENING_PORT;
-        else if (strcmp(token, "%URI") == 0 || strcmp(token, "%>ru") == 0)
-            format->type = Format::LFT_CLIENT_REQ_URI;
-        else if (strcmp(token, "%DST") == 0 || strcmp(token, "%>rd") == 0)
-            format->type = Format::LFT_CLIENT_REQ_URLDOMAIN;
-        else if (strcmp(token, "%PROTO") == 0 || strcmp(token, "%>rs") == 0)
-            format->type = Format::LFT_CLIENT_REQ_URLSCHEME;
-        else if (strcmp(token, "%PORT") == 0) // XXX: add a logformat token
-            format->type = Format::LFT_CLIENT_REQ_URLPORT;
-        else if (strcmp(token, "%PATH") == 0 || strcmp(token, "%>rp") == 0)
-            format->type = Format::LFT_CLIENT_REQ_URLPATH;
-        else if (strcmp(token, "%METHOD") == 0 || strcmp(token, "%>rm") == 0)
-            format->type = Format::LFT_CLIENT_REQ_METHOD;
-#if USE_OPENSSL
-        else if (strcmp(token, "%USER_CERT") == 0)
-            format->type = Format::LFT_EXT_ACL_USER_CERT_RAW;
-        else if (strcmp(token, "%USER_CERTCHAIN") == 0)
-            format->type = Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW;
-        else if (strncmp(token, "%USER_CERT_", 11) == 0) {
-            format->type = Format::LFT_EXT_ACL_USER_CERT;
-            format->header = xstrdup(token + 11);
+        *fmt = new Format::Token;
+
+        // compatibility for old tokens incompatible with Format::Token syntax
+#if USE_OPENSSL // dont bother if we dont have to.
+        if (strncmp(token, "%USER_CERT_", 11) == 0) {
+            (*fmt)->type = Format::LFT_EXT_ACL_USER_CERT;
+            (*fmt)->data.string = xstrdup(token + 11);
+            (*fmt)->data.header.header = (*fmt)->data.string;
         } else if (strncmp(token, "%USER_CA_CERT_", 14) == 0) {
-            format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
-            format->header = xstrdup(token + 14);
+            (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT;
+            (*fmt)->data.string = xstrdup(token + 14);
+            (*fmt)->data.header.header = (*fmt)->data.string;
         } else if (strncmp(token, "%CA_CERT_", 9) == 0) {
             debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
-            format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
-            format->header = xstrdup(token + 9);
-        } else if (strcmp(token, "%ssl::>sni") == 0)
-            format->type = Format::LFT_SSL_CLIENT_SNI;
-        else if (strcmp(token, "%ssl::<cert_subject") == 0)
-            format->type = Format::LFT_SSL_SERVER_CERT_SUBJECT;
-        else if (strcmp(token, "%ssl::<cert_issuer") == 0)
-            format->type = Format::LFT_SSL_SERVER_CERT_ISSUER;
+            (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT;
+            (*fmt)->data.string = xstrdup(token + 9);
+            (*fmt)->data.header.header = (*fmt)->data.string;
+        } else
 #endif
+        {
+            // we can use the Format::Token::parse() method since it
+            // only pulls off one token. Since we already checked
+            // for '%' prefix above this is guaranteed to be a token.
+            const size_t len = (*fmt)->parse(token, &quote);
+            assert(len == strlen(token));
+        }
+
+        // process special token-specific actions (only if necessary)
 #if USE_AUTH
-        else if (strcmp(token, "%EXT_USER") == 0 || strcmp(token, "%ue") == 0)
-            format->type = Format::LFT_USER_EXTERNAL;
+        if ((*fmt)->type == Format::LFT_USER_LOGIN)
+            a->require_auth = true;
 #endif
-        else if (strcmp(token, "%EXT_LOG") == 0 || strcmp(token, "%ea") == 0)
-            format->type = Format::LFT_EXT_LOG;
-        else if (strcmp(token, "%TAG") == 0  || strcmp(token, "%et") == 0)
-            format->type = Format::LFT_TAG;
-        else if (strcmp(token, "%ACL") == 0)
-            format->type = Format::LFT_EXT_ACL_NAME;
-        else if (strcmp(token, "%DATA") == 0)
-            format->type = Format::LFT_EXT_ACL_DATA;
-        else if (strcmp(token, "%%") == 0)
-            format->type = Format::LFT_PERCENT;
-        else {
-            debugs(0, DBG_CRITICAL, "ERROR: Unknown Format token " << token);
-            self_destruct();
-        }
 
-        *p = format;
-        p = &format->next;
+        fmt = &((*fmt)->next);
         token = ConfigParser::NextToken();
     }
 
     /* There must be at least one format token */
-    if (!a->format)
+    if (!a->format.format)
         self_destruct();
 
     /* helper */
@@ -499,72 +370,7 @@ dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl
         if (node->quote == external_acl::QUOTE_METHOD_SHELL)
             storeAppendPrintf(sentry, " protocol=2.5");
 
-        for (external_acl_format::Pointer format = node->format; format!= NULL; format = format->next) {
-            switch (format->type) {
-
-            case Format::LFT_ADAPTED_REQUEST_HEADER:
-                storeAppendPrintf(sentry, " %%>ha{%s}", format->header);
-                break;
-
-            case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM:
-                storeAppendPrintf(sentry, " %%>ha{%s:%s}", format->header, format->member);
-                break;
-
-            case Format::LFT_REPLY_HEADER:
-                storeAppendPrintf(sentry, " %%<h{%s}", format->header);
-                break;
-
-            case Format::LFT_REPLY_HEADER_ELEM:
-                storeAppendPrintf(sentry, " %%<h{%s:%s}", format->header, format->member);
-                break;
-
-#define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \
-            case Format::LFT_##a: \
-                storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \
-                break
-#if USE_AUTH
-                DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN," %%ul");
-#endif
-#if USE_IDENT
-
-                DUMP_EXT_ACL_TYPE_FMT(USER_IDENT," %%ui");
-#endif
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS," %%>a");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT," %%>p");
-#if USE_SQUID_EUI
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48," %%SRCEUI48");
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64," %%SRCEUI64");
-#endif
-                DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP," %%>la");
-                DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT," %%>lp");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI," %%>ru");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN," %%>rd");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME," %%>rs");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT," %%>rP");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH," %%>rp");
-                DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD," %%>rm");
-#if USE_OPENSSL
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW, " %%USER_CERT_RAW");
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW, " %%USER_CERTCHAIN_RAW");
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT, " %%USER_CERT_%s", format->header);
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT, " %%USER_CA_CERT_%s", format->header);
-                DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI, "%%ssl::>sni");
-                DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_SUBJECT, "%%ssl::<cert_subject");
-                DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_ISSUER, "%%ssl::<cert_issuer");
-#endif
-#if USE_AUTH
-                DUMP_EXT_ACL_TYPE_FMT(USER_EXTERNAL," %%ue");
-#endif
-                DUMP_EXT_ACL_TYPE_FMT(EXT_LOG," %%ea");
-                DUMP_EXT_ACL_TYPE_FMT(TAG," %%et");
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_NAME," %%ACL");
-                DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_DATA," %%DATA");
-                DUMP_EXT_ACL_TYPE_FMT(PERCENT, " %%%%");
-            default:
-                fatal("unknown external_acl format error");
-                break;
-            }
-        }
+        node->format.dump(sentry, NULL, false);
 
         for (word = node->cmdline; word; word = word->next)
             storeAppendPrintf(sentry, " %s", word->key);
@@ -891,230 +697,21 @@ static char *
 makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
 {
     static MemBuf mb;
-    char buf[256];
-    int first = 1;
-    wordlist *arg;
-    HttpRequest *request = ch->request;
-    HttpReply *reply = ch->reply;
-    mb.reset();
-    bool data_used = false;
-
-    for (external_acl_format::Pointer format = acl_data->def->format; format != NULL; format = format->next) {
-        const char *str = NULL;
-        String sb;
-
-        switch (format->type) {
-#if USE_AUTH
-        case Format::LFT_USER_LOGIN:
-            // if this ACL line was the cause of credentials fetch
-            // they may not already be in the checklist
-            if (ch->auth_user_request == NULL && ch->request)
-                ch->auth_user_request = ch->request->auth_user_request;
-
-            if (ch->auth_user_request != NULL)
-                str = ch->auth_user_request->username();
-            break;
-#endif
-#if USE_IDENT
-        case Format::LFT_USER_IDENT:
-            str = ch->rfc931;
-
-            if (!str || !*str) {
-                // if we fail to go async, we still return NULL and the caller
-                // will detect the failure in ACLExternal::match().
-                (void)ch->goAsync(IdentLookup::Instance());
-                return NULL;
-            }
-
-            break;
-#endif
-
-        case Format::LFT_CLIENT_IP_ADDRESS:
-            str = ch->src_addr.toStr(buf,sizeof(buf));
-            break;
-
-        case Format::LFT_CLIENT_PORT:
-            snprintf(buf, sizeof(buf), "%d", request->client_addr.port());
-            str = buf;
-            break;
-
-#if USE_SQUID_EUI
-        case Format::LFT_EXT_ACL_CLIENT_EUI48:
-            if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL &&
-                    request->clientConnectionManager->clientConnection->remoteEui48.encode(buf, sizeof(buf)))
-                str = buf;
-            break;
 
-        case Format::LFT_EXT_ACL_CLIENT_EUI64:
-            if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL &&
-                    request->clientConnectionManager->clientConnection->remoteEui64.encode(buf, sizeof(buf)))
-                str = buf;
-            break;
-#endif
-
-        case Format::LFT_LOCAL_LISTENING_IP:
-            str = request->my_addr.toStr(buf, sizeof(buf));
-            break;
-
-        case Format::LFT_LOCAL_LISTENING_PORT:
-            snprintf(buf, sizeof(buf), "%d", request->my_addr.port());
-            str = buf;
-            break;
-
-        case Format::LFT_CLIENT_REQ_URI:
-            str = urlCanonical(request);
-            break;
-
-        case Format::LFT_CLIENT_REQ_URLDOMAIN:
-            str = request->GetHost();
-            break;
-
-        case Format::LFT_CLIENT_REQ_URLSCHEME:
-            str = request->url.getScheme().c_str();
-            break;
-
-        case Format::LFT_CLIENT_REQ_URLPORT:
-            snprintf(buf, sizeof(buf), "%d", request->port);
-            str = buf;
-            break;
+    // check for special case tokens in the format
+    for (Format::Token *t = acl_data->def->format.format; t ; t = t->next) {
 
-        case Format::LFT_CLIENT_REQ_URLPATH:
-            str = request->urlpath.termedBuf();
-            break;
-
-        case Format::LFT_CLIENT_REQ_METHOD: {
-            const SBuf &s = request->method.image();
-            sb.append(s.rawContent(), s.length());
+        if (t->type == Format::LFT_EXT_ACL_NAME) {
+            // setup for %ACL
+            safe_free(ch->al->_private.lastAclName);
+            ch->al->_private.lastAclName = xstrdup(acl_data->name);
         }
-        str = sb.termedBuf();
-        break;
-
-        case Format::LFT_ADAPTED_REQUEST_HEADER:
-            if (format->header_id == -1)
-                sb = request->header.getByName(format->header);
-            else
-                sb = request->header.getStrOrList(format->header_id);
-            str = sb.termedBuf();
-            break;
-
-        case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM:
-            if (format->header_id == -1)
-                sb = request->header.getByNameListMember(format->header, format->member, format->separator);
-            else
-                sb = request->header.getListMember(format->header_id, format->member, format->separator);
-            str = sb.termedBuf();
-            break;
-
-        case Format::LFT_REPLY_HEADER:
-            if (reply) {
-                if (format->header_id == -1)
-                    sb = reply->header.getByName(format->header);
-                else
-                    sb = reply->header.getStrOrList(format->header_id);
-                str = sb.termedBuf();
-            }
-            break;
 
-        case Format::LFT_REPLY_HEADER_ELEM:
-            if (reply) {
-                if (format->header_id == -1)
-                    sb = reply->header.getByNameListMember(format->header, format->member, format->separator);
-                else
-                    sb = reply->header.getListMember(format->header_id, format->member, format->separator);
-                str = sb.termedBuf();
-            }
-            break;
-
-#if USE_OPENSSL
-
-        case Format::LFT_EXT_ACL_USER_CERT_RAW:
-
-            if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
-                SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
-
-                if (ssl)
-                    str = sslGetUserCertificatePEM(ssl);
-            }
-
-            break;
-
-        case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW:
-
-            if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
-                SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
-
-                if (ssl)
-                    str = sslGetUserCertificateChainPEM(ssl);
-            }
-
-            break;
-
-        case Format::LFT_EXT_ACL_USER_CERT:
-
-            if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
-                SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
-
-                if (ssl)
-                    str = sslGetUserAttribute(ssl, format->header);
-            }
-
-            break;
-
-        case Format::LFT_EXT_ACL_USER_CA_CERT:
-
-            if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
-                SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
-
-                if (ssl)
-                    str = sslGetCAAttribute(ssl, format->header);
-            }
-
-            break;
-
-        case Format::LFT_SSL_CLIENT_SNI:
-            if (ch->conn() != NULL) {
-                if (Ssl::ServerBump * srvBump = ch->conn()->serverBump()) {
-                    if (!srvBump->clientSni.isEmpty())
-                        str = srvBump->clientSni.c_str();
-                }
-            }
-            break;
-
-        case Format::LFT_SSL_SERVER_CERT_SUBJECT:
-        case Format::LFT_SSL_SERVER_CERT_ISSUER: {
-            X509 *serverCert = NULL;
-            if (ch->serverCert.get())
-                serverCert = ch->serverCert.get();
-            else if (ch->conn() && ch->conn()->serverBump())
-                serverCert = ch->conn()->serverBump()->serverCert.get();
-
-            if (serverCert) {
-                if (format->type == Format::LFT_SSL_SERVER_CERT_SUBJECT)
-                    str = Ssl::GetX509UserAttribute(serverCert, "DN");
-                else
-                    str = Ssl::GetX509CAAttribute(serverCert, "DN");
-            }
-            break;
-        }
-
-#endif
-#if USE_AUTH
-        case Format::LFT_USER_EXTERNAL:
-            str = request->extacl_user.termedBuf();
-            break;
-#endif
-        case Format::LFT_EXT_LOG:
-            str = request->extacl_log.termedBuf();
-            break;
-        case Format::LFT_TAG:
-            str = request->tag.termedBuf();
-            break;
-        case Format::LFT_EXT_ACL_NAME:
-            str = acl_data->name;
-            break;
-        case Format::LFT_EXT_ACL_DATA:
-            data_used = true;
-            for (arg = acl_data->arguments; arg; arg = arg->next) {
+        if (t->type == Format::LFT_EXT_ACL_NAME) {
+            // setup string for %DATA
+            SBuf sb;
+            bool first = true;
+            for (auto arg = acl_data->arguments; arg; arg = arg->next) {
                 if (!first)
                     sb.append(" ", 1);
 
@@ -1129,58 +726,22 @@ makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
                     mb2.clean();
                 }
 
-                first = 0;
+                first = false;
             }
-            break;
-        case Format::LFT_PERCENT:
-            str = "%";
-            break;
-
-        default:
-            // TODO: replace this function with Format::assemble()
-            // For now die on unsupported logformat codes.
-            fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format->type);
-            break;
         }
 
-        if (str)
-            if (!*str)
-                str = NULL;
-
-        if (!str)
-            str = "-";
-
-        if (!first)
-            mb.append(" ", 1);
-
-        if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
-            const char *quoted = rfc1738_escape(str);
-            mb.append(quoted, strlen(quoted));
-        } else {
-            strwordquote(&mb, str);
-        }
-
-        sb.clean();
-
-        first = 0;
-    }
-
-    if (!data_used) {
-        for (arg = acl_data->arguments; arg; arg = arg->next) {
-            if (!first)
-                mb.append(" ", 1);
-
-            if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
-                const char *quoted = rfc1738_escape(arg->key);
-                mb.append(quoted, strlen(quoted));
-            } else {
-                strwordquote(&mb, arg->key);
+        if (t->type == Format::LFT_USER_IDENT) {
+            if (!ch->rfc931 || !*ch->rfc931) {
+                // if we fail to go async, we still return NULL and the caller
+                // will detect the failure in ACLExternal::match().
+                (void)ch->goAsync(IdentLookup::Instance());
+                return NULL;
             }
-
-            first = 0;
         }
     }
 
+    acl_data->def->format.assemble(mb, ch->al, 0);
+
     return mb.buf;
 }
 
index 74a63433400b074f9804baf04d733ffec49304ca..00f220e816bee3b1cefe73c442989ebce0115966 100644 (file)
@@ -88,14 +88,15 @@ Format::Format::parse(const char *def)
 }
 
 void
-Format::Format::dump(StoreEntry * entry, const char *directiveName)
+Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
 {
     debugs(46, 4, HERE);
 
     // loop rather than recursing to conserve stack space.
-    for (Format *fmt = this; fmt; fmt = fmt->next) {
+    for (const Format *fmt = this; fmt; fmt = fmt->next) {
         debugs(46, 3, HERE << "Dumping format definition for " << fmt->name);
-        storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
+        if (directiveName)
+            storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
 
         for (Token *t = fmt->format; t; t = t->next) {
             if (t->type == LFT_STRING)
@@ -251,7 +252,8 @@ Format::Format::dump(StoreEntry * entry, const char *directiveName)
             }
         }
 
-        entry->append("\n", 1);
+        if (eol)
+            entry->append("\n", 1);
     }
 
 }
@@ -370,11 +372,29 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                     al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
                 out = tmp;
             }
-#else
-            out = "-";
 #endif
             break;
 
+#if USE_SQUID_EUI
+        case LFT_EXT_ACL_CLIENT_EUI48:
+            if (al->request && al->request->clientConnectionManager.valid() &&
+                    al->request->clientConnectionManager->clientConnection != NULL &&
+                    al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
+                al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
+                out = tmp;
+            }
+            break;
+
+        case LFT_EXT_ACL_CLIENT_EUI64:
+            if (al->request && al->request->clientConnectionManager.valid() &&
+                    al->request->clientConnectionManager->clientConnection != NULL &&
+                    !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
+                al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
+                out = tmp;
+            }
+            break;
+#endif
+
         case LFT_SERVER_IP_ADDRESS:
             if (al->hier.tcpServer != NULL) {
                 out = al->hier.tcpServer->remote.toStr(tmp,sizeof(tmp));
@@ -837,7 +857,11 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_USER_EXTERNAL:
-            out = strOrNull(al->cache.extuser);
+            if (al->request && al->request->extacl_user.size())
+                out = al->request->extacl_user.termedBuf();
+
+            if (!out)
+                out = strOrNull(al->cache.extuser);
             break;
 
         /* case LFT_USER_REALM: */
@@ -902,8 +926,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             } else
 #endif
                 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
-                    if (al->request->errDetail > ERR_DETAIL_START  &&
-                            al->request->errDetail < ERR_DETAIL_MAX)
+                    if (al->request->errDetail > ERR_DETAIL_START && al->request->errDetail < ERR_DETAIL_MAX)
                         out = errorDetailName(al->request->errDetail);
                     else {
                         if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
@@ -1130,8 +1153,48 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
             // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
             out = Ssl::bumpMode(mode);
-            break;
         }
+            break;
+
+        case LFT_EXT_ACL_USER_CERT_RAW:
+            if (al->request) {
+                ConnStateData *conn = al->request->clientConnectionManager.get();
+                if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
+                    if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
+                        out = sslGetUserCertificatePEM(ssl);
+                }
+            }
+            break;
+
+        case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
+            if (al->request) {
+                ConnStateData *conn = al->request->clientConnectionManager.get();
+                if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
+                    if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
+                        out = sslGetUserCertificatePEM(ssl);
+                }
+            }
+            break;
+
+        case LFT_EXT_ACL_USER_CERT:
+            if (al->request) {
+                ConnStateData *conn = al->request->clientConnectionManager.get();
+                if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
+                    if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
+                        out = sslGetUserAttribute(ssl, format->data.header.header);
+                }
+            }
+            break;
+
+        case LFT_EXT_ACL_USER_CA_CERT:
+            if (al->request) {
+                ConnStateData *conn = al->request->clientConnectionManager.get();
+                if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
+                    if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
+                       out = sslGetCAAttribute(ssl, format->data.header.header);
+                }
+            }
+            break;
 
         case LFT_SSL_USER_CERT_SUBJECT:
             if (X509 *cert = al->cache.sslClientCert.get()) {
@@ -1150,6 +1213,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                 }
             }
             break;
+
         case LFT_SSL_CLIENT_SNI:
             if (al->request && al->request->clientConnectionManager.valid()) {
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
@@ -1217,18 +1281,12 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             out = "%";
             break;
 
-        // XXX: external_acl_type format tokens which are not output by logformat.
-        // They are listed here because the switch requires
-        // every ByteCode_t to be explicitly enumerated.
-        // But do not output due to lack of access to the values.
-        case LFT_EXT_ACL_USER_CERT_RAW:
-        case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
-        case LFT_EXT_ACL_USER_CERT:
-        case LFT_EXT_ACL_USER_CA_CERT:
-        case LFT_EXT_ACL_CLIENT_EUI48:
-        case LFT_EXT_ACL_CLIENT_EUI64:
         case LFT_EXT_ACL_NAME:
+            out = al->_private.lastAclName;
+            break;
+
         case LFT_EXT_ACL_DATA:
+            out = al->_private.lastAclData;
             break;
         }
 
index 4ae6e668db8cb0545b89df9bba3ef1fd0da698ca..4fd86bf76ff068d738730a4e5596d74ee59d9f23 100644 (file)
@@ -51,7 +51,7 @@ public:
     void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const;
 
     /// dump this whole list of formats into the provided StoreEntry
-    void dump(StoreEntry * entry, const char *directiveName);
+    void dump(StoreEntry * entry, const char *directiveName, bool eol = true) const;
 
     char *name;
     Token *format;
index 8e0b31b49e26273b561ab1e3316786b2166ff746..85e7cb54ec9ff3e65c983adb66f3f0d649eb0acb 100644 (file)
@@ -145,6 +145,30 @@ static TokenTableEntry TokenTableMisc[] = {
     TokenTableEntry("err_detail", LFT_SQUID_ERROR_DETAIL ),
     TokenTableEntry("note", LFT_NOTE ),
     TokenTableEntry("credentials", LFT_CREDENTIALS),
+    /*
+     * Legacy external_acl_type format tokens
+     */
+    TokenTableEntry("ACL", LFT_EXT_ACL_NAME),
+    TokenTableEntry("DATA", LFT_EXT_ACL_DATA),
+    TokenTableEntry("DST", LFT_CLIENT_REQ_URLDOMAIN),
+    TokenTableEntry("EXT_LOG", LFT_EXT_LOG),
+    TokenTableEntry("EXT_USER", LFT_USER_EXTERNAL),
+    TokenTableEntry("IDENT", LFT_USER_IDENT),
+    TokenTableEntry("LOGIN", LFT_USER_LOGIN),
+    TokenTableEntry("METHOD", LFT_CLIENT_REQ_METHOD),
+    TokenTableEntry("MYADDR", LFT_LOCAL_LISTENING_IP),
+    TokenTableEntry("MYPORT", LFT_LOCAL_LISTENING_PORT),
+    TokenTableEntry("PATH", LFT_CLIENT_REQ_URLPATH),
+    TokenTableEntry("PORT", LFT_CLIENT_REQ_URLPORT),
+    TokenTableEntry("PROTO", LFT_CLIENT_REQ_URLSCHEME),
+    TokenTableEntry("SRCEUI48", LFT_EXT_ACL_CLIENT_EUI48),
+    TokenTableEntry("SRCEUI64", LFT_EXT_ACL_CLIENT_EUI64),
+    TokenTableEntry("SRCPORT", LFT_CLIENT_PORT),
+    TokenTableEntry("SRC", LFT_CLIENT_IP_ADDRESS), // keep after longer SRC* tokens
+    TokenTableEntry("TAG", LFT_TAG),
+    TokenTableEntry("URI", LFT_CLIENT_REQ_URI),
+    TokenTableEntry("USER_CERTCHAIN", LFT_EXT_ACL_USER_CERTCHAIN_RAW),
+    TokenTableEntry("USER_CERT", LFT_EXT_ACL_USER_CERT_RAW),
     TokenTableEntry(NULL, LFT_NONE)        /* this must be last */
 };
 
@@ -202,7 +226,6 @@ void
 Format::Token::Init()
 {
     // TODO standard log tokens
-    // TODO external ACL fmt tokens
 
 #if USE_ADAPTATION
     TheConfig.registerTokens(String("adapt"),::Format::TokenTableAdapt);
@@ -335,6 +358,7 @@ Format::Token::parse(const char *def, Quoting *quoting)
             cur = endp;
         }
 
+        // when {arg} field is before the token (old logformat syntax)
         if (*cur == '{') {
             char *cp;
             ++cur;
@@ -395,6 +419,21 @@ Format::Token::parse(const char *def, Quoting *quoting)
             fatalf("Can't parse configuration token: '%s'\n", def);
         }
 
+        // when {arg} field is after the token (old external_acl_type token syntax)
+        // but accept only if there was none before the token
+        if (*cur == '{' && !data.string) {
+            char *cp;
+            ++cur;
+            l = strcspn(cur, "}");
+            cp = (char *)xmalloc(l + 1);
+            xstrncpy(cp, cur, l + 1);
+            data.string = cp;
+            cur += l;
+
+            if (*cur == '}')
+                ++cur;
+        }
+
         if (*cur == ' ') {
             space = true;
             ++cur;