| /** @file |
| |
| This file implements the LogAccess class. |
| |
| @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 "proxy/logging/LogAccess.h" |
| |
| #include "records/RecCore.h" |
| #include "tscore/Version.h" |
| #include "proxy/hdrs/MIME.h" |
| #include "proxy/logging/TransactionLogData.h" |
| #include "iocore/utils/Machine.h" |
| #include "proxy/logging/LogFormat.h" |
| #include "proxy/logging/LogBuffer.h" |
| #include "records/RecHttp.h" |
| #include "swoc/BufferWriter.h" |
| #include "tscore/Encoding.h" |
| #include "tscore/ink_inet.h" |
| #include "tscore/ink_base64.h" |
| |
| char INVALID_STR[] = "!INVALID_STR!"; |
| |
| // should be at least 22 bytes to always accommodate a converted |
| // MgmtInt, MgmtIntCounter or MgmtFloat. 22 bytes is enough for 64 bit |
| // ints + sign + eos, and enough for %e floating point representation |
| // + eos |
| // |
| #define MARSHAL_RECORD_LENGTH 32 |
| |
| namespace |
| { |
| DbgCtl dbg_ctl_log_escape{"log-escape"}; |
| DbgCtl dbg_ctl_log_resolve{"log-resolve"}; |
| DbgCtl dbg_ctl_log_unmarshal_orun{"log-unmarshal-orun"}; // Overrun of unmarshaling destination buffer. |
| DbgCtl dbg_ctl_log_unmarshal_data{"log-unmarshal-data"}; // Error in txn data when unmarshalling. |
| |
| int |
| marshal_http_version_string(char *buf, HTTPHdr *response) |
| { |
| HTTPVersion version = response ? response->version_get() : HTTP_INVALID; |
| char str[8]; |
| int str_len = |
| snprintf(str, sizeof(str), "%u.%u", static_cast<unsigned>(version.get_major()), static_cast<unsigned>(version.get_minor())); |
| int len = LogAccess::padded_length(str_len + 1); |
| |
| ink_release_assert(str_len > 0 && str_len < static_cast<int>(sizeof(str))); |
| if (buf) { |
| LogAccess::marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| } // end anonymous namespace |
| |
| #define DBG_UNMARSHAL_DEST_OVERRUN Dbg(dbg_ctl_log_unmarshal_orun, "Unmarshal destination buffer overrun."); |
| |
| /*------------------------------------------------------------------------- |
| LogAccess |
| |
| Initialize from TransactionLogData supplied by the caller. |
| -------------------------------------------------------------------------*/ |
| |
| LogAccess::LogAccess(TransactionLogData &data) : m_data(&data) {} |
| |
| void |
| LogAccess::init() |
| { |
| ink_assert(m_data != nullptr); |
| |
| m_client_request = m_data->get_client_request(); |
| |
| const char *url_str = m_data->get_client_req_url_str(); |
| int url_len = m_data->get_client_req_url_len(); |
| if (url_str != nullptr && url_len > 0) { |
| m_client_req_url_len = url_len; |
| m_client_req_url_str = m_arena.str_alloc(m_client_req_url_len + 1); |
| memcpy(m_client_req_url_str, url_str, m_client_req_url_len); |
| m_client_req_url_str[m_client_req_url_len] = '\0'; |
| |
| m_client_req_url_canon_str = |
| Encoding::escapify_url(&m_arena, m_client_req_url_str, m_client_req_url_len, &m_client_req_url_canon_len); |
| } |
| |
| const char *path_str = m_data->get_client_req_url_path_str(); |
| int path_len = m_data->get_client_req_url_path_len(); |
| if (path_str != nullptr && path_len > 0) { |
| m_client_req_url_path_len = path_len; |
| char *path_copy = m_arena.str_alloc(m_client_req_url_path_len + 1); |
| memcpy(path_copy, path_str, m_client_req_url_path_len); |
| path_copy[m_client_req_url_path_len] = '\0'; |
| m_client_req_url_path_str = path_copy; |
| } else { |
| m_client_req_url_path_str = nullptr; |
| m_client_req_url_path_len = 0; |
| } |
| |
| m_proxy_response = m_data->get_proxy_response(); |
| |
| char *ct_str = m_data->get_proxy_resp_content_type_str(); |
| int ct_len = m_data->get_proxy_resp_content_type_len(); |
| if (ct_str != nullptr && ct_len > 0) { |
| m_proxy_resp_content_type_str = ct_str; |
| m_proxy_resp_content_type_len = ct_len; |
| LogUtils::remove_content_type_attributes(m_proxy_resp_content_type_str, &m_proxy_resp_content_type_len); |
| } |
| |
| char *reason_str = m_data->get_proxy_resp_reason_phrase_str(); |
| int reason_len = m_data->get_proxy_resp_reason_phrase_len(); |
| if (reason_str != nullptr && reason_len > 0) { |
| m_proxy_resp_reason_phrase_str = reason_str; |
| m_proxy_resp_reason_phrase_len = reason_len; |
| } |
| |
| m_proxy_request = m_data->get_proxy_request(); |
| m_server_response = m_data->get_server_response(); |
| m_cache_response = m_data->get_cache_response(); |
| } |
| |
| int |
| LogAccess::marshal_proxy_host_name(char *buf) |
| { |
| int len = 0; |
| char const *str = nullptr; |
| |
| if (Machine *machine = Machine::instance(); machine) { |
| str = machine->host_name.c_str(); |
| len = machine->host_name.length(); |
| } |
| |
| len = INK_ALIGN_DEFAULT(len + 1); |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_host_ip(char *buf) |
| { |
| return marshal_ip(buf, &Machine::instance()->ip.sa); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_process_uuid(char *buf) |
| { |
| int len = padded_length(TS_UUID_STRING_LEN + 1); |
| |
| if (buf) { |
| const char *str = const_cast<char *>(Machine::instance()->process_uuid.getString()); |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_process_sfid(char *buf) |
| { |
| char const *str = nullptr; |
| int len = 0; |
| |
| if (Machine *machine = Machine::instance(); machine) { |
| std::string_view snowflake_id = machine->process_snowflake_id->get_string(); |
| str = snowflake_id.data(); |
| len = snowflake_id.length(); |
| } |
| |
| len = INK_ALIGN_DEFAULT(len + 1); |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_config_int_var(char *config_var, char *buf) |
| { |
| if (buf) { |
| int64_t val; |
| val = RecGetRecordInt(config_var).value_or(0); |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_config_str_var(char *config_var, char *buf) |
| { |
| auto str{RecGetRecordStringAlloc(config_var)}; |
| auto c_str{ats_as_c_str(str)}; |
| int len = LogAccess::padded_strlen(c_str); |
| if (buf) { |
| marshal_str(buf, c_str, len); |
| } |
| return len; |
| } |
| |
| // To allow for a generic marshal_record function, rather than |
| // multiple functions (one per data type) we always marshal a record |
| // as a string of a fixed length. We use a fixed length because the |
| // marshal_record function can be called with a null *buf to request |
| // the length of the record, and later with a non-null *buf to |
| // actually request the record to be inserted in the buffer, and both |
| // calls should return the same number of characters. If we did not |
| // enforce a fixed size, this would not necessarily be the case |
| // because records --statistics in particular-- can potentially change |
| // between one call and the other. |
| // |
| int |
| LogAccess::marshal_record(char *record, char *buf) |
| { |
| const unsigned int max_chars = MARSHAL_RECORD_LENGTH; |
| |
| if (nullptr == buf) { |
| return max_chars; |
| } |
| |
| const char *record_not_found_msg = "RECORD_NOT_FOUND"; |
| const unsigned int record_not_found_chars = ::strlen(record_not_found_msg) + 1; |
| |
| char ascii_buf[max_chars]; |
| const char *out_buf; |
| unsigned int num_chars; |
| |
| #define LOG_INTEGER RECD_INT |
| #define LOG_COUNTER RECD_COUNTER |
| #define LOG_FLOAT RECD_FLOAT |
| #define LOG_STRING RECD_STRING |
| |
| RecDataT stype = RECD_NULL; |
| bool found = false; |
| |
| // Since, for now at least, String metrics are still in librecords, do that lookup |
| // first, and only do the new metrics lookup on a miss. |
| if (RecGetRecordDataType(record, &stype) != REC_ERR_OKAY) { |
| ts::Metrics &metrics = ts::Metrics::instance(); |
| ts::Metrics::IdType mid = metrics[record]; |
| |
| if (mid != ts::Metrics::NOT_FOUND) { |
| int64_t val = metrics[mid].load(); |
| |
| out_buf = int64_to_str(ascii_buf, max_chars, val, &num_chars); |
| ink_assert(out_buf); |
| } else { |
| out_buf = "INVALID_RECORD"; |
| num_chars = ::strlen(out_buf) + 1; |
| } |
| } else { |
| if (LOG_INTEGER == stype || LOG_COUNTER == stype) { |
| // we assume MgmtInt and MgmtIntCounter are int64_t for the |
| // conversion below, if this ever changes we should modify |
| // accordingly |
| // |
| ink_assert(sizeof(int64_t) >= sizeof(RecInt) && sizeof(int64_t) >= sizeof(RecCounter)); |
| |
| // so that a 64 bit integer will fit (including sign and eos) |
| // |
| ink_assert(max_chars > 21); |
| |
| auto tmp{LOG_INTEGER == stype ? RecGetRecordInt(record) : RecGetRecordCounter(record)}; |
| auto found{tmp.has_value()}; |
| |
| if (found) { |
| out_buf = int64_to_str(ascii_buf, max_chars, tmp.value(), &num_chars); |
| ink_assert(out_buf); |
| } else { |
| out_buf = const_cast<char *>(record_not_found_msg); |
| num_chars = record_not_found_chars; |
| } |
| } else if (LOG_FLOAT == stype) { |
| // we assume MgmtFloat is at least a float for the conversion below |
| // (the conversion itself assumes a double because of the %e) |
| // if this ever changes we should modify accordingly |
| // |
| ink_assert(sizeof(double) >= sizeof(RecFloat)); |
| |
| auto val{RecGetRecordFloat(record)}; |
| found = val.has_value(); |
| |
| if (found) { |
| // snprintf does not support "%e" in the format |
| // and we want to use "%e" because it is the most concise |
| // notation |
| |
| num_chars = snprintf(ascii_buf, sizeof(ascii_buf), "%e", val.value()) + 1; // include eos |
| |
| // the "%e" field above should take 13 characters at most |
| // |
| ink_assert(num_chars <= max_chars); |
| |
| // the following should never be true |
| // |
| if (num_chars > max_chars) { |
| // data does not fit, output asterisks |
| out_buf = "***"; |
| num_chars = ::strlen(out_buf) + 1; |
| } else { |
| out_buf = ascii_buf; |
| } |
| } else { |
| out_buf = const_cast<char *>(record_not_found_msg); |
| num_chars = record_not_found_chars; |
| } |
| } else if (LOG_STRING == stype) { |
| if (auto sv{RecGetRecordString(record, ascii_buf, sizeof(ascii_buf))}; sv) { |
| if (sv.value().length() > 0) { |
| num_chars = sv.value().length() + 1; |
| if (num_chars == max_chars) { |
| // truncate string and write ellipsis at the end |
| ascii_buf[max_chars - 1] = 0; |
| ascii_buf[max_chars - 2] = '.'; |
| ascii_buf[max_chars - 3] = '.'; |
| ascii_buf[max_chars - 4] = '.'; |
| } |
| out_buf = ascii_buf; |
| } else { |
| out_buf = "NULL"; |
| num_chars = ::strlen(out_buf) + 1; |
| } |
| } else { |
| out_buf = const_cast<char *>(record_not_found_msg); |
| num_chars = record_not_found_chars; |
| } |
| } else { |
| out_buf = "INVALID_MgmtType"; |
| num_chars = ::strlen(out_buf) + 1; |
| ink_assert(!"invalid MgmtType for requested record"); |
| } |
| } |
| |
| ink_assert(num_chars <= max_chars); |
| memcpy(buf, out_buf, num_chars); |
| |
| return max_chars; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::marshal_str |
| |
| Copy the given string to the destination buffer, including the trailing |
| NULL. For binary formatting, we need the NULL to distinguish the end of |
| the string, and we'll remove it for ascii formatting. |
| ASSUMES dest IS NOT NULL. |
| The array pointed to by dest must be at least padded_len in length. |
| -------------------------------------------------------------------------*/ |
| |
| void |
| LogAccess::marshal_str(char *dest, const char *source, int padded_len) |
| { |
| if (source == nullptr || source[0] == 0 || padded_len == 0) { |
| source = DEFAULT_STR; |
| } |
| ink_strlcpy(dest, source, padded_len); |
| |
| #ifdef DEBUG |
| // |
| // what padded_len should be, if there is no padding, is strlen()+1. |
| // if not, then we needed to pad and should touch the intermediate |
| // bytes to avoid UMR errors when the buffer is written. |
| // |
| size_t real_len = (::strlen(source) + 1); |
| while (static_cast<int>(real_len) < padded_len) { |
| dest[real_len] = '$'; |
| real_len++; |
| } |
| #endif |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_client_request, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::marshal_mem |
| |
| This is a version of marshal_str that works with unterminated strings. |
| In this case, we'll copy the buffer and then add a trailing null that |
| the rest of the system assumes. |
| -------------------------------------------------------------------------*/ |
| |
| void |
| LogAccess::marshal_mem(char *dest, const char *source, int actual_len, int padded_len) |
| { |
| if (source == nullptr || source[0] == 0 || actual_len == 0) { |
| source = DEFAULT_STR; |
| actual_len = DEFAULT_STR_LEN; |
| ink_assert(actual_len < padded_len); |
| } |
| memcpy(dest, source, actual_len); |
| dest[actual_len] = 0; // add terminating null |
| |
| #ifdef DEBUG |
| // |
| // what len should be, if there is no padding, is strlen()+1. |
| // if not, then we needed to pad and should touch the intermediate |
| // bytes to avoid UMR errors when the buffer is written. |
| // |
| int real_len = actual_len + 1; |
| while (real_len < padded_len) { |
| dest[real_len] = '$'; |
| real_len++; |
| } |
| #endif |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::marshal_ip |
| |
| Marshal an IP address in a reasonably compact way. If the address isn't |
| valid (NULL or not IP) then marshal an invalid address record. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_ip(char *dest, sockaddr const *ip) |
| { |
| LogFieldIpStorage data; |
| int len = sizeof(data._ip); |
| if (nullptr == ip) { |
| data._ip._family = AF_UNSPEC; |
| } else if (ats_is_ip4(ip)) { |
| if (dest) { |
| data._ip4._family = AF_INET; |
| data._ip4._addr = ats_ip4_addr_cast(ip); |
| } |
| len = sizeof(data._ip4); |
| } else if (ats_is_ip6(ip)) { |
| if (dest) { |
| data._ip6._family = AF_INET6; |
| data._ip6._addr = ats_ip6_addr_cast(ip); |
| } |
| len = sizeof(data._ip6); |
| } else if (ats_is_unix(ip)) { |
| if (dest) { |
| data._un._family = AF_UNIX; |
| strncpy(data._un._path, ats_unix_cast(ip)->sun_path, TS_UNIX_SIZE); |
| } |
| len = sizeof(data._un); |
| } else { |
| data._ip._family = AF_UNSPEC; |
| } |
| |
| if (dest) { |
| memcpy(dest, &data, len); |
| } |
| return INK_ALIGN_DEFAULT(len); |
| } |
| |
| int |
| LogAccess::marshal_custom_field(char *buf, LogField::Type type, const LogField::CustomMarshalFunc &plugin_marshal_func) |
| { |
| void *sm = m_data->http_sm_for_plugins(); |
| if (sm == nullptr) { |
| switch (type) { |
| case LogField::Type::sINT: |
| [[fallthrough]]; |
| case LogField::Type::dINT: |
| if (buf) { |
| marshal_int(buf, 0); |
| } |
| return INK_MIN_ALIGN; |
| case LogField::Type::STRING: { |
| int len = LogAccess::padded_strlen(nullptr); |
| if (buf) { |
| marshal_str(buf, nullptr, len); |
| } |
| return len; |
| } |
| case LogField::Type::IP: |
| return marshal_ip(buf, nullptr); |
| case LogField::Type::N_TYPES: |
| [[fallthrough]]; |
| case LogField::Type::INVALID: |
| break; |
| } |
| } |
| int len = plugin_marshal_func(sm, buf); |
| return LogAccess::padded_length(len); |
| } |
| |
| HTTPHdr * |
| LogAccess::header_for_container(LogField::Container container) const |
| { |
| switch (container) { |
| case LogField::CQH: |
| case LogField::ECQH: |
| return m_client_request; |
| |
| case LogField::PSH: |
| case LogField::EPSH: |
| return m_proxy_response; |
| |
| case LogField::PQH: |
| case LogField::EPQH: |
| return m_proxy_request; |
| |
| case LogField::SSH: |
| case LogField::ESSH: |
| return m_server_response; |
| |
| case LogField::CSSH: |
| case LogField::ECSSH: |
| return m_cache_response; |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool |
| LogAccess::has_http_header_field(LogField::Container container, const char *field) const |
| { |
| if (HTTPHdr const *header = header_for_container(container); header != nullptr) { |
| if (header->field_find(std::string_view{field}) != nullptr) { |
| return true; |
| } |
| } |
| |
| if (container == LogField::SSH && strcmp(field, "Transfer-Encoding") == 0) { |
| if (!m_data->get_server_response_transfer_encoding().empty()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| inline int |
| LogAccess::unmarshal_with_map(int64_t code, char *dest, int len, const Ptr<LogFieldAliasMap> &map, const char *msg) |
| { |
| long int codeStrLen = 0; |
| |
| switch (map->asString(code, dest, len, reinterpret_cast<size_t *>(&codeStrLen))) { |
| case LogFieldAliasMap::INVALID_INT: |
| if (msg) { |
| const int bufSize = 64; |
| char invalidCodeMsg[bufSize]; |
| codeStrLen = snprintf(invalidCodeMsg, 64, "%s(%" PRId64 ")", msg, code); |
| if (codeStrLen < bufSize && codeStrLen < len) { |
| ink_strlcpy(dest, invalidCodeMsg, len); |
| } else { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| codeStrLen = -1; |
| } |
| } else { |
| codeStrLen = -1; |
| } |
| break; |
| case LogFieldAliasMap::BUFFER_TOO_SMALL: |
| DBG_UNMARSHAL_DEST_OVERRUN |
| codeStrLen = -1; |
| break; |
| } |
| |
| return codeStrLen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_int |
| |
| Return the integer pointed at by the buffer and advance the buffer |
| pointer past the int. The int will be converted back to host byte order. |
| -------------------------------------------------------------------------*/ |
| |
| int64_t |
| LogAccess::unmarshal_int(char **buf) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| int64_t val; |
| |
| // TODO: this used to do nthol, do we need to worry? TS-1156. |
| val = *(reinterpret_cast<int64_t *>(*buf)); |
| *buf += INK_MIN_ALIGN; |
| return val; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_proxy_response, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| /*------------------------------------------------------------------------- |
| unmarshal_itoa |
| |
| This routine provides a fast conversion from a binary int to a string. |
| It returns the number of characters formatted. "dest" must point to the |
| LAST character of an array large enough to store the complete formatted |
| number. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_itoa(int64_t val, char *dest, int field_width, char leading_char) |
| { |
| ink_assert(dest != nullptr); |
| |
| char *p = dest; |
| bool negative = false; |
| |
| if (val < 0) { |
| negative = true; |
| val = -val; |
| } |
| |
| do { |
| *p-- = '0' + (val % 10); |
| val /= 10; |
| } while (val); |
| |
| while (dest - p < field_width) { |
| *p-- = leading_char; |
| } |
| |
| if (negative) { |
| *p-- = '-'; |
| } |
| |
| return static_cast<int>(dest - p); |
| } |
| |
| /*------------------------------------------------------------------------- |
| unmarshal_itox |
| |
| This routine provides a fast conversion from a binary int to a hex string. |
| It returns the number of characters formatted. "dest" must point to the |
| LAST character of an array large enough to store the complete formatted |
| number. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_itox(int64_t val, char *dest, int field_width, char leading_char) |
| { |
| ink_assert(dest != nullptr); |
| |
| char *p = dest; |
| static char table[] = "0123456789abcdef?"; |
| |
| for (int i = 0; i < static_cast<int>(sizeof(int64_t) * 2); i++) { |
| *p-- = table[val & 0xf]; |
| val >>= 4; |
| } |
| while (dest - p < field_width) { |
| *p-- = leading_char; |
| } |
| |
| return static_cast<int64_t>(dest - p); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_int_to_str |
| |
| Return the string representation of the integer pointed at by buf. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_int_to_str(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| char val_buf[128]; |
| int64_t val = unmarshal_int(buf); |
| int val_len = unmarshal_itoa(val, val_buf + 127); |
| |
| if (val_len < len) { |
| memcpy(dest, val_buf + 128 - val_len, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_milestone_diff |
| |
| Unmarshal a milestone difference value. Returns "-" when the |
| marshalled value is -1 (the "missing" sentinel from difference_msec, |
| meaning one or both milestones were unset). Other negative values |
| (reversed milestone order) are preserved as numeric output for |
| debugging. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_milestone_diff(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| int64_t val = unmarshal_int(buf); |
| if (val == -1) { |
| if (len >= 1) { |
| dest[0] = '-'; |
| return 1; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| char val_buf[128]; |
| int val_len = unmarshal_itoa(val, val_buf + 127); |
| if (val_len < len) { |
| memcpy(dest, val_buf + 128 - val_len, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_int_to_str_hex |
| |
| Return the string representation (hexadecimal) of the integer pointed at by buf. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_int_to_str_hex(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| char val_buf[128]; |
| int64_t val = unmarshal_int(buf); |
| int val_len = unmarshal_itox(val, val_buf + 127); |
| |
| if (val_len < len) { |
| memcpy(dest, val_buf + 128 - val_len, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_proxy_request, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| namespace |
| { |
| class EscLookup |
| { |
| public: |
| static const char NO_ESCAPE{'\0'}; |
| static const char LONG_ESCAPE{'\x01'}; |
| |
| static char |
| result(char c) |
| { |
| return _lu.table[static_cast<unsigned char>(c)]; |
| } |
| |
| private: |
| struct _LUT { |
| _LUT(); |
| |
| char table[1 << 8]; |
| }; |
| |
| inline static _LUT const _lu; |
| }; |
| |
| EscLookup::_LUT::_LUT() |
| { |
| for (unsigned i = 0; i < ' '; ++i) { |
| table[i] = LONG_ESCAPE; |
| } |
| for (unsigned i = '\x7f'; i < sizeof(table); ++i) { |
| table[i] = LONG_ESCAPE; |
| } |
| |
| // Short escapes. |
| // |
| table[static_cast<int>('\b')] = 'b'; |
| table[static_cast<int>('\t')] = 't'; |
| table[static_cast<int>('\n')] = 'n'; |
| table[static_cast<int>('\f')] = 'f'; |
| table[static_cast<int>('\r')] = 'r'; |
| table[static_cast<int>('\\')] = '\\'; |
| table[static_cast<int>('\"')] = '"'; |
| table[static_cast<int>('/')] = '/'; |
| } |
| |
| char |
| nibble(int nib) |
| { |
| return nib >= 0xa ? 'a' + (nib - 0xa) : '0' + nib; |
| } |
| |
| int |
| escape_json(char *dest, const char *buf, int len) |
| { |
| int escaped_len = 0; |
| |
| for (int i = 0; i < len; i++) { |
| char c = buf[i]; |
| char ec = EscLookup::result(c); |
| if (__builtin_expect(EscLookup::NO_ESCAPE == ec, 1)) { |
| if (dest) { |
| if (escaped_len + 1 > len) { |
| break; |
| } |
| *dest++ = c; |
| } |
| escaped_len++; |
| |
| } else if (EscLookup::LONG_ESCAPE == ec) { |
| if (dest) { |
| if (escaped_len + 6 > len) { |
| break; |
| } |
| *dest++ = '\\'; |
| *dest++ = 'u'; |
| *dest++ = '0'; |
| *dest++ = '0'; |
| *dest++ = nibble(static_cast<unsigned char>(c) >> 4); |
| *dest++ = nibble(c & 0x0f); |
| } |
| escaped_len += 6; |
| |
| } else { // Short escape. |
| if (dest) { |
| if (escaped_len + 2 > len) { |
| break; |
| } |
| *dest++ = '\\'; |
| *dest++ = ec; |
| } |
| escaped_len += 2; |
| } |
| } // end for |
| return escaped_len; |
| } |
| |
| int |
| unmarshal_str_json(char **buf, char *dest, int len, LogSlice *slice) |
| { |
| Dbg(dbg_ctl_log_escape, "unmarshal_str_json start, len=%d, slice=%p", len, slice); |
| |
| char *val_buf = *buf; |
| int val_len = static_cast<int>(::strlen(val_buf)); |
| int escaped_len = escape_json(nullptr, val_buf, val_len); |
| |
| *buf += LogAccess::padded_strlen(val_buf); // this is how it was stored |
| |
| if (slice && slice->m_enable) { |
| int offset, n; |
| |
| n = slice->toStrOffset(escaped_len, &offset); |
| Dbg(dbg_ctl_log_escape, "unmarshal_str_json start, n=%d, offset=%d", n, offset); |
| if (n <= 0) { |
| return 0; |
| } |
| |
| if (n >= len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| return escape_json(dest, (val_buf + offset), n); |
| } |
| |
| if (escaped_len < len) { |
| escape_json(dest, val_buf, escaped_len); |
| return escaped_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| } // end anonymous namespace |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_str |
| |
| Retrieve the string from the location pointed at by the buffer and |
| advance the pointer past the string. The local strlen function is used |
| to advance the pointer, thus matching the corresponding strlen that was |
| used to lay the string into the buffer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_str(char **buf, char *dest, int len, LogSlice *slice, LogEscapeType escape_type) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| if (LOG_ESCAPE_JSON == escape_type) { |
| return unmarshal_str_json(buf, dest, len, slice); |
| } |
| |
| char *val_buf = *buf; |
| int val_len = static_cast<int>(::strlen(val_buf)); |
| |
| *buf += LogAccess::padded_strlen(val_buf); // this is how it was stored |
| |
| if (slice && slice->m_enable) { |
| int offset, n; |
| |
| n = slice->toStrOffset(val_len, &offset); |
| if (n <= 0) { |
| return 0; |
| } |
| |
| if (n >= len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| memcpy(dest, (val_buf + offset), n); |
| return n; |
| } |
| |
| if (val_len < len) { |
| memcpy(dest, val_buf, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| int |
| LogAccess::unmarshal_ttmsf(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| int64_t val = unmarshal_int(buf); |
| int val_len = snprintf(dest, len, "%" PRId64 ".%03d", val / 1000, int((val < 0 ? -val : val) % 1000)); |
| if (val_len >= len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| return val_len; |
| } |
| |
| int |
| LogAccess::unmarshal_int_to_date_str(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| int64_t value = unmarshal_int(buf); |
| char *strval = LogUtils::timestamp_to_date_str(value); |
| int strlen = static_cast<int>(::strlen(strval)); |
| |
| if (strlen > len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| memcpy(dest, strval, strlen); |
| return strlen; |
| } |
| |
| int |
| LogAccess::unmarshal_int_to_time_str(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| int64_t value = unmarshal_int(buf); |
| char *strval = LogUtils::timestamp_to_time_str(value); |
| int strlen = static_cast<int>(::strlen(strval)); |
| |
| if (strlen > len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| memcpy(dest, strval, strlen); |
| return strlen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_resp_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_server_response, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_int_to_netscape_str(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| int64_t value = unmarshal_int(buf); |
| char *strval = LogUtils::timestamp_to_netscape_str(value); |
| int strlen = static_cast<int>(::strlen(strval)); |
| |
| if (strlen > len) { |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| memcpy(dest, strval, strlen); |
| return strlen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_http_method |
| |
| Retrieve the int pointed at by the buffer and treat as an HttpMethod |
| enumerated type. Then lookup the string representation for that enum and |
| return the string. Advance the buffer pointer past the enum. |
| -------------------------------------------------------------------------*/ |
| /* |
| int |
| LogAccess::unmarshal_http_method (char **buf, char *dest, int len) |
| { |
| return unmarshal_str (buf, dest, len); |
| } |
| */ |
| |
| int |
| LogAccess::marshal_cache_resp_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_cache_response, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_http_version |
| |
| The HTTP version is marshalled as a "major.minor" string. Prefix it with |
| "HTTP/" for the established text log representation. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_http_version(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| static constexpr std::string_view prefix{"HTTP/"}; |
| |
| char *version = *buf; |
| int version_len = static_cast<int>(strlen(version)); |
| int value_len = static_cast<int>(prefix.size()) + version_len; |
| |
| *buf += padded_strlen(version); |
| if (value_len <= len) { |
| memcpy(dest, prefix.data(), prefix.size()); |
| memcpy(dest + prefix.size(), version, version_len); |
| return value_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_http_status |
| |
| An http response status code (pssc,sssc) is just an INT, but it's always |
| formatted with three digits and leading zeros. So, we need a special |
| version of unmarshal_int_to_str that does this leading zero formatting. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_http_status(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| char val_buf[128]; |
| int64_t val = unmarshal_int(buf); |
| int val_len = unmarshal_itoa(val, val_buf + 127, 3, '0'); |
| if (val_len < len) { |
| memcpy(dest, val_buf + 128 - val_len, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_ip |
| |
| Retrieve an IP address directly. |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::unmarshal_ip(char **buf, IpEndpoint *dest) |
| { |
| int len = sizeof(LogFieldIp); // of object processed. |
| |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| LogFieldIp *raw = reinterpret_cast<LogFieldIp *>(*buf); |
| if (AF_INET == raw->_family) { |
| LogFieldIp4 *ip4 = static_cast<LogFieldIp4 *>(raw); |
| ats_ip4_set(dest, ip4->_addr); |
| len = sizeof(*ip4); |
| } else if (AF_INET6 == raw->_family) { |
| LogFieldIp6 *ip6 = static_cast<LogFieldIp6 *>(raw); |
| ats_ip6_set(dest, ip6->_addr); |
| len = sizeof(*ip6); |
| } else if (AF_UNIX == raw->_family) { |
| LogFieldUn *un = static_cast<LogFieldUn *>(raw); |
| ats_unix_set(dest, un->_path, TS_UNIX_SIZE); |
| len = sizeof(*un); |
| } else { |
| ats_ip_invalidate(dest); |
| } |
| len = INK_ALIGN_DEFAULT(len); |
| *buf += len; |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_ip_to_str |
| |
| Retrieve the IP addresspointed at by the buffer and convert to a |
| string in standard format. The string is written to @a dest and its |
| length (not including nul) is returned. @a *buf is advanced. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_ip_to_str(char **buf, char *dest, int len) |
| { |
| IpEndpoint ip; |
| |
| if (len > 0) { |
| unmarshal_ip(buf, &ip); |
| if (!ats_is_ip(&ip) && !ats_is_unix(ip)) { |
| *dest = '0'; |
| Dbg(dbg_ctl_log_unmarshal_data, "Invalid IP address"); |
| return 1; |
| } else if (ats_ip_ntop(&ip, dest, len)) { |
| return static_cast<int>(::strlen(dest)); |
| } |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_ip_to_hex |
| |
| Retrieve the int pointed at by the buffer and treat as an IP |
| address. Convert to a string in byte oriented hexadecimal and |
| return the string. Advance the buffer pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_ip_to_hex(char **buf, char *dest, int len) |
| { |
| IpEndpoint ip; |
| |
| if (len > 0) { |
| unmarshal_ip(buf, &ip); |
| if (!ats_is_ip(&ip) && !ats_is_unix(ip)) { |
| *dest = '0'; |
| Dbg(dbg_ctl_log_unmarshal_data, "Invalid IP address"); |
| return 1; |
| } else { |
| return ats_ip_to_hex(&ip.sa, dest, len); |
| } |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_hierarchy |
| |
| Retrieve the int pointed at by the buffer and treat as a |
| SquidHierarchyCode. Use this as an index into the local string |
| conversion tables and return the string equivalent to the enum. |
| Advance the buffer pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_hierarchy(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "INVALID_CODE")); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_finish_status |
| |
| Retrieve the int pointed at by the buffer and treat as a finish code. |
| Use the enum as an index into a string table and return the string equiv |
| of the enum. Advance the pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_finish_status(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "UNKNOWN_FINISH_CODE")); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_cache_code |
| |
| Retrieve the int pointed at by the buffer and treat as a SquidLogCode. |
| Use this to index into the local string tables and return the string |
| equiv of the enum. Advance the pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_cache_code(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "ERROR_UNKNOWN")); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_cache_hit_miss |
| |
| Retrieve the int pointed at by the buffer and treat as a SquidHitMissCode. |
| Use this to index into the local string tables and return the string |
| equiv of the enum. Advance the pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_cache_hit_miss(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "HIT_MISS_UNKNOWN")); |
| } |
| |
| int |
| LogAccess::unmarshal_cache_write_code(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "UNKNOWN_CACHE_WRITE_CODE")); |
| } |
| |
| int |
| LogAccess::unmarshal_record(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| char *val_buf = *buf; |
| int val_len = static_cast<int>(::strlen(val_buf)); |
| *buf += MARSHAL_RECORD_LENGTH; // this is how it was stored |
| if (val_len < len) { |
| memcpy(dest, val_buf, val_len); |
| return val_len; |
| } |
| DBG_UNMARSHAL_DEST_OVERRUN |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| resolve_logfield_string |
| |
| This function resolves the given custom log format string using the given |
| LogAccess context and returns the resulting string, which is ats_malloc'd. |
| The caller is responsible for ats_free'ing the return result. If there are |
| any problems, NULL is returned. |
| -------------------------------------------------------------------------*/ |
| char * |
| resolve_logfield_string(LogAccess *context, const char *format_str) |
| { |
| if (!context) { |
| Dbg(dbg_ctl_log_resolve, "No context to resolve?"); |
| return nullptr; |
| } |
| |
| if (!format_str) { |
| Dbg(dbg_ctl_log_resolve, "No format to resolve?"); |
| return nullptr; |
| } |
| |
| Dbg(dbg_ctl_log_resolve, "Resolving: %s", format_str); |
| |
| // |
| // Divide the format string into two parts: one for the printf-style |
| // string and one for the symbols. |
| // |
| char *printf_str = nullptr; |
| char *fields_str = nullptr; |
| int n_fields = LogFormat::parse_format_string(format_str, &printf_str, &fields_str); |
| |
| // |
| // Perhaps there were no fields to resolve? Then just return the |
| // format_str. Nothing to free here either. |
| // |
| if (!n_fields) { |
| Dbg(dbg_ctl_log_resolve, "No fields found; returning copy of format_str"); |
| ats_free(printf_str); |
| ats_free(fields_str); |
| return ats_strdup(format_str); |
| } |
| |
| Dbg(dbg_ctl_log_resolve, "%d fields: %s", n_fields, fields_str); |
| Dbg(dbg_ctl_log_resolve, "printf string: %s", printf_str); |
| |
| LogFieldList fields; |
| bool contains_aggregates; |
| int field_count = LogFormat::parse_symbol_string(fields_str, &fields, &contains_aggregates); |
| |
| if (field_count != n_fields) { |
| Error("format_str contains %d invalid field symbols", n_fields - field_count); |
| ats_free(printf_str); |
| ats_free(fields_str); |
| return nullptr; |
| } |
| // |
| // Ok, now marshal the data out of the LogAccess object and into a |
| // temporary storage buffer. Make sure the LogAccess context is |
| // initialized first. |
| // |
| Dbg(dbg_ctl_log_resolve, "Marshaling data from LogAccess into buffer ..."); |
| context->init(); |
| unsigned bytes_needed = fields.marshal_len(context); |
| char *buf = static_cast<char *>(ats_malloc(bytes_needed)); |
| unsigned bytes_used = fields.marshal(context, buf); |
| |
| ink_assert(bytes_needed == bytes_used); |
| Dbg(dbg_ctl_log_resolve, " %u bytes marshalled", bytes_used); |
| |
| // |
| // Now we can "unmarshal" the data from the buffer into a string, |
| // combining it with the data from the printf string. The problem is, |
| // we're not sure how much space it will take when it's unmarshalled. |
| // So, we'll just guess. |
| // |
| char *result = static_cast<char *>(ats_malloc(8192)); |
| unsigned bytes_resolved = |
| LogBuffer::resolve_custom_entry(&fields, printf_str, buf, result, 8191, LogUtils::timestamp(), 0, LOG_SEGMENT_VERSION); |
| ink_assert(bytes_resolved < 8192); |
| |
| if (!bytes_resolved) { |
| ats_free(result); |
| result = nullptr; |
| } else { |
| result[bytes_resolved] = 0; // NULL terminate |
| } |
| |
| ats_free(printf_str); |
| ats_free(fields_str); |
| ats_free(buf); |
| |
| return result; |
| } |
| void |
| LogAccess::set_client_req_url(char *buf, int len) |
| { |
| if (buf) { |
| m_client_req_url_len = std::min(len, m_client_req_url_len); |
| ink_strlcpy(m_client_req_url_str, buf, m_client_req_url_len + 1); |
| } |
| } |
| |
| void |
| LogAccess::set_client_req_url_canon(char *buf, int len) |
| { |
| if (buf) { |
| m_client_req_url_canon_len = std::min(len, m_client_req_url_canon_len); |
| ink_strlcpy(m_client_req_url_canon_str, buf, m_client_req_url_canon_len + 1); |
| } |
| } |
| |
| void |
| LogAccess::set_client_req_unmapped_url_canon(char *buf, int len) |
| { |
| if (buf && m_client_req_unmapped_url_canon_str) { |
| // m_client_req_unmapped_url_canon_str is not necessarily null terminated. |
| m_client_req_unmapped_url_canon_len = std::min(len, m_client_req_unmapped_url_canon_len); |
| memcpy(m_client_req_unmapped_url_canon_str, buf, m_client_req_unmapped_url_canon_len); |
| } |
| } |
| |
| void |
| LogAccess::set_client_req_unmapped_url_path(char *buf, int len) |
| { |
| if (buf && m_client_req_unmapped_url_path_str) { |
| m_client_req_unmapped_url_path_len = std::min(len, m_client_req_unmapped_url_path_len); |
| ink_strlcpy(m_client_req_unmapped_url_path_str, buf, m_client_req_unmapped_url_path_len + 1); |
| } |
| } |
| |
| void |
| LogAccess::set_client_req_unmapped_url_host(char *buf, int len) |
| { |
| if (buf && m_client_req_unmapped_url_host_str) { |
| m_client_req_unmapped_url_host_len = std::min(len, m_client_req_unmapped_url_host_len); |
| ink_strlcpy(m_client_req_unmapped_url_host_str, buf, m_client_req_unmapped_url_host_len + 1); |
| } |
| } |
| |
| void |
| LogAccess::set_client_req_url_path(char *buf, int len) |
| { |
| //?? use m_client_req_unmapped_url_path_str for now..may need to enhance later.. |
| this->set_client_req_unmapped_url_path(buf, len); |
| } |
| |
| /*------------------------------------------------------------------------- |
| The marshalling routines ... |
| |
| Header pointers may be null; each routine must guard before use. |
| -------------------------------------------------------------------------*/ |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_plugin_identity_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_plugin_id()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_plugin_identity_tag(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| const char *tag = m_data->get_plugin_tag(); |
| |
| if (!tag) { |
| tag = "*"; |
| } else { |
| len = LogAccess::padded_strlen(tag); |
| } |
| |
| if (buf) { |
| marshal_str(buf, tag, len); |
| } |
| |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_client_host_ip(char *buf) |
| { |
| return marshal_ip(buf, m_data->get_client_addr()); |
| } |
| |
| int |
| LogAccess::marshal_remote_host_ip(char *buf) |
| { |
| return marshal_ip(buf, m_data->get_client_src_addr()); |
| } |
| |
| int |
| LogAccess::marshal_host_interface_ip(char *buf) |
| { |
| return marshal_ip(buf, m_data->get_client_dst_addr()); |
| } |
| |
| int |
| LogAccess::marshal_client_host_ip_verified(char *buf) |
| { |
| if (m_data->get_verified_client_addr()) { |
| return marshal_ip(buf, m_data->get_verified_client_addr()); |
| } |
| |
| return marshal_client_host_ip(buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_cache_lookup_url_canon(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| |
| validate_lookup_url(); |
| if (m_cache_lookup_url_canon_str == INVALID_STR) { |
| // If the lookup URL isn't populated, we'll fall back to the request URL. |
| len = marshal_client_req_url_canon(buf); |
| } else { |
| len = padded_length(m_cache_lookup_url_canon_len + 1); // +1 for eos |
| if (buf) { |
| marshal_mem(buf, m_cache_lookup_url_canon_str, m_cache_lookup_url_canon_len, len); |
| } |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_sni_server_name(char *buf) |
| { |
| const char *sni = m_data->get_sni_server_name(); |
| const char *server_name_cstr = sni ? sni : ""; |
| int len = LogAccess::padded_strlen(server_name_cstr); |
| if (buf) { |
| marshal_str(buf, server_name_cstr, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_provided_cert(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_provided_cert()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_provided_cert(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_server_connection_provided_cert()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_version_build_number(char *buf) |
| { |
| auto &version = AppVersionInfo::get_version(); |
| int len = LogAccess::padded_strlen(version.build_number()); |
| if (buf) { |
| marshal_str(buf, version.build_number(), len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_version_string(char *buf) |
| { |
| auto &version = AppVersionInfo::get_version(); |
| int len = LogAccess::padded_strlen(version.version()); |
| if (buf) { |
| marshal_str(buf, version.version(), len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_protocol_version(char *buf) |
| { |
| const char *version_str; |
| switch (m_data->get_pp_version()) { |
| case 1: |
| version_str = "V1"; |
| break; |
| case 2: |
| version_str = "V2"; |
| break; |
| default: |
| version_str = "-"; |
| break; |
| } |
| int len = LogAccess::padded_strlen(version_str); |
| if (buf) { |
| marshal_str(buf, version_str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_protocol_src_ip(char *buf) |
| { |
| sockaddr const *ip = nullptr; |
| if (m_data->get_pp_version() != 0) { |
| ip = m_data->get_pp_src_addr(); |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_protocol_dst_ip(char *buf) |
| { |
| sockaddr const *ip = nullptr; |
| if (m_data->get_pp_version() != 0) { |
| ip = m_data->get_pp_dst_addr(); |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| int |
| LogAccess::marshal_proxy_protocol_authority(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| std::string_view authority = m_data->get_pp_authority(); |
| |
| if (!authority.empty()) { |
| len = padded_length(static_cast<int>(authority.size()) + 1); |
| if (buf) { |
| marshal_mem(buf, authority.data(), static_cast<int>(authority.size()), len); |
| } |
| } |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_proxy_protocol_tls_cipher(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| std::string_view cipher = m_data->get_pp_tls_cipher(); |
| |
| if (!cipher.empty()) { |
| len = padded_length(static_cast<int>(cipher.size()) + 1); |
| if (buf) { |
| marshal_mem(buf, cipher.data(), static_cast<int>(cipher.size()), len); |
| } |
| } else { |
| if (buf) { |
| marshal_mem(buf, nullptr, 0, len); |
| } |
| } |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_proxy_protocol_tls_version(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| std::string_view version = m_data->get_pp_tls_version(); |
| |
| if (!version.empty()) { |
| len = padded_length(static_cast<int>(version.size()) + 1); |
| if (buf) { |
| marshal_mem(buf, version.data(), static_cast<int>(version.size()), len); |
| } |
| } else { |
| if (buf) { |
| marshal_mem(buf, nullptr, 0, len); |
| } |
| } |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_proxy_protocol_tls_group(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| std::string_view group = m_data->get_pp_tls_group(); |
| |
| if (!group.empty()) { |
| len = padded_length(static_cast<int>(group.size()) + 1); |
| if (buf) { |
| marshal_mem(buf, group.data(), static_cast<int>(group.size()), len); |
| } |
| } else { |
| if (buf) { |
| marshal_mem(buf, nullptr, 0, len); |
| } |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_host_port(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_port()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_remote_host_port(char *buf) |
| { |
| if (buf) { |
| sockaddr const *src = m_data->get_client_src_addr(); |
| uint16_t port = src ? ats_ip_port_host_order(src) : 0; |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| user authenticated to the proxy (RFC931) |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_auth_user_name(char *buf) |
| { |
| char *str = nullptr; |
| int len = INK_MIN_ALIGN; |
| |
| // Jira TS-40: |
| // NOTE: Authentication related code and modules were removed/disabled. |
| // Uncomment code path below when re-added/enabled. |
| /*if (auth_params.user_name) { |
| str = auth_params.user_name; |
| len = LogAccess::strlen(str); |
| } */ |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Private utility function to validate m_client_req_unmapped_url_canon_str & |
| m_client_req_unmapped_url_canon_len fields. |
| -------------------------------------------------------------------------*/ |
| |
| void |
| LogAccess::validate_unmapped_url() |
| { |
| char *unmapped_url_str = m_data->get_unmapped_url_str(); |
| int unmapped_url_len = m_data->get_unmapped_url_len(); |
| |
| if (unmapped_url_str == nullptr) { |
| m_client_req_unmapped_url_canon_str = INVALID_STR; |
| return; |
| } |
| |
| if (m_client_req_unmapped_url_canon_str == nullptr) { |
| // prevent multiple validations |
| m_client_req_unmapped_url_canon_str = INVALID_STR; |
| |
| if (unmapped_url_len > 0) { |
| if (unmapped_url_str && unmapped_url_str[0] != 0) { |
| m_client_req_unmapped_url_canon_str = |
| Encoding::escapify_url(&m_arena, unmapped_url_str, unmapped_url_len, &m_client_req_unmapped_url_canon_len); |
| } |
| } |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| Private utility function to validate m_client_req_unmapped_url_path_str & |
| m_client_req_unmapped_url_path_len fields. |
| -------------------------------------------------------------------------*/ |
| |
| void |
| LogAccess::validate_unmapped_url_path() |
| { |
| if (m_client_req_unmapped_url_path_str == nullptr && m_client_req_unmapped_url_host_str == nullptr) { |
| // Use unmapped canonical URL as default |
| m_client_req_unmapped_url_path_str = m_client_req_unmapped_url_canon_str; |
| m_client_req_unmapped_url_path_len = m_client_req_unmapped_url_canon_len; |
| // In case the code below fails, we prevent it from being used. |
| m_client_req_unmapped_url_host_str = INVALID_STR; |
| |
| if (m_client_req_unmapped_url_path_len >= 6) { // xxx:// - minimum schema size |
| int len; |
| char *c = |
| static_cast<char *>(memchr((void *)m_client_req_unmapped_url_path_str, ':', m_client_req_unmapped_url_path_len - 1)); |
| |
| if (c && (len = static_cast<int>(c - m_client_req_unmapped_url_path_str)) <= 5) { // 5 - max schema size |
| if (len + 2 <= m_client_req_unmapped_url_canon_len && c[1] == '/' && c[2] == '/') { |
| len += 3; // Skip "://" |
| m_client_req_unmapped_url_host_str = &m_client_req_unmapped_url_canon_str[len]; |
| m_client_req_unmapped_url_host_len = m_client_req_unmapped_url_path_len - len; |
| // Attempt to find first '/' in the path |
| if (m_client_req_unmapped_url_host_len > 0 && |
| (c = static_cast<char *>( |
| memchr((void *)m_client_req_unmapped_url_host_str, '/', m_client_req_unmapped_url_host_len))) != nullptr) { |
| m_client_req_unmapped_url_host_len = static_cast<int>(c - m_client_req_unmapped_url_host_str); |
| m_client_req_unmapped_url_path_str = &m_client_req_unmapped_url_host_str[m_client_req_unmapped_url_host_len]; |
| m_client_req_unmapped_url_path_len = m_client_req_unmapped_url_path_len - len - m_client_req_unmapped_url_host_len; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| Private utility function to validate m_cache_lookup_url_canon_str & |
| m_cache_lookup__url_canon_len fields. |
| -------------------------------------------------------------------------*/ |
| void |
| LogAccess::validate_lookup_url() |
| { |
| char *lookup_url_str = m_data->get_cache_lookup_url_str(); |
| int lookup_url_len = m_data->get_cache_lookup_url_len(); |
| |
| if (lookup_url_str == nullptr) { |
| m_cache_lookup_url_canon_str = INVALID_STR; |
| return; |
| } |
| |
| if (m_cache_lookup_url_canon_str == nullptr) { |
| // prevent multiple validations |
| m_cache_lookup_url_canon_str = INVALID_STR; |
| |
| if (lookup_url_len > 0) { |
| if (lookup_url_str && lookup_url_str[0] != 0) { |
| m_cache_lookup_url_canon_str = |
| Encoding::escapify_url(&m_arena, lookup_url_str, lookup_url_len, &m_cache_lookup_url_canon_len); |
| } |
| } |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| This is the method, url, and version all rolled into one. Use the |
| respective marshalling routines to do the job. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_text(char *buf) |
| { |
| int len = marshal_client_req_http_method(nullptr) + marshal_client_req_url(nullptr) + marshal_client_req_http_version(nullptr); |
| |
| if (buf) { |
| int offset = 0; |
| offset += marshal_client_req_http_method(&buf[offset]); |
| offset += marshal_client_req_url(&buf[offset]); |
| offset += marshal_client_req_http_version(&buf[offset]); |
| len = offset; |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_timestamp_sec(char *buf) |
| { |
| return marshal_milestone_fmt_sec(TS_MILESTONE_UA_BEGIN, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_timestamp_ms(char *buf) |
| { |
| return marshal_milestone_fmt_ms(TS_MILESTONE_UA_BEGIN, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_http_method(char *buf) |
| { |
| std::string_view str; |
| int plen = INK_MIN_ALIGN; |
| |
| if (m_client_request) { |
| str = m_client_request->method_get(); |
| } |
| |
| if (str.empty()) { |
| str = m_data->get_method(); |
| } |
| |
| // calculate the padded length only if the actual length |
| // is not zero. We don't want the padded length to be zero |
| // because marshal_mem should write the DEFAULT_STR to the |
| // buffer if str is nil, and we need room for this. |
| // |
| if (!str.empty()) { |
| plen = padded_length(static_cast<int>(str.length()) + 1); // +1 for trailing 0 |
| } |
| |
| if (buf) { |
| marshal_mem(buf, str.data(), static_cast<int>(str.length()), plen); |
| } |
| return plen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_url(char *buf) |
| { |
| int len = padded_length(m_client_req_url_len + 1); // +1 for trailing 0 |
| |
| if (buf) { |
| marshal_mem(buf, m_client_req_url_str, m_client_req_url_len, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_url_canon(char *buf) |
| { |
| int len = padded_length(m_client_req_url_canon_len + 1); |
| |
| if (buf) { |
| marshal_mem(buf, m_client_req_url_canon_str, m_client_req_url_canon_len, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_unmapped_url_canon(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| |
| validate_unmapped_url(); |
| if (m_client_req_unmapped_url_canon_str == INVALID_STR) { |
| // If the unmapped URL isn't populated, we'll fall back to the original |
| // client URL. This helps for example server intercepts to continue to |
| // log the requests, even when there is no remap rule for it. |
| len = marshal_client_req_url_canon(buf); |
| } else { |
| len = padded_length(m_client_req_unmapped_url_canon_len + 1); // +1 for eos |
| if (buf) { |
| marshal_mem(buf, m_client_req_unmapped_url_canon_str, m_client_req_unmapped_url_canon_len, len); |
| } |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_unmapped_url_path(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| |
| validate_unmapped_url(); |
| validate_unmapped_url_path(); |
| |
| if (m_client_req_unmapped_url_path_str == INVALID_STR) { |
| len = marshal_client_req_url_path(buf); |
| } else { |
| len = padded_length(m_client_req_unmapped_url_path_len + 1); // +1 for eos |
| if (buf) { |
| marshal_mem(buf, m_client_req_unmapped_url_path_str, m_client_req_unmapped_url_path_len, len); |
| } |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_unmapped_url_host(char *buf) |
| { |
| validate_unmapped_url(); |
| validate_unmapped_url_path(); |
| |
| int len = padded_length(m_client_req_unmapped_url_host_len + 1); // +1 for eos |
| if (buf) { |
| marshal_mem(buf, m_client_req_unmapped_url_host_str, m_client_req_unmapped_url_host_len, len); |
| } |
| |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_client_req_url_path(char *buf) |
| { |
| int len = padded_length(m_client_req_url_path_len + 1); |
| if (buf) { |
| marshal_mem(buf, m_client_req_url_path_str, m_client_req_url_path_len, len); |
| } |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_client_req_url_scheme(char *buf) |
| { |
| const char *str = nullptr; |
| int alen = 0; |
| int plen = INK_MIN_ALIGN; |
| int scheme = m_data->get_orig_scheme(); |
| std::string_view scheme_sv = m_data->get_scheme(); |
| |
| if (scheme >= 0) { |
| str = hdrtoken_index_to_wks(scheme); |
| alen = hdrtoken_index_to_length(scheme); |
| } else if (!scheme_sv.empty()) { |
| str = scheme_sv.data(); |
| alen = static_cast<int>(scheme_sv.size()); |
| } else { |
| str = "UNKNOWN"; |
| alen = 7; |
| } |
| plen = padded_length(alen + 1); |
| |
| if (buf) { |
| marshal_mem(buf, str, alen, plen); |
| } |
| |
| return plen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| For this one we're going to marshal two INTs, one the first representing |
| the major number and the second representing the minor. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_http_version(char *buf) |
| { |
| if (buf) { |
| if (m_client_request) { |
| HTTPVersion versionObject = m_client_request->version_get(); |
| marshal_int(buf, versionObject.get_major()); |
| marshal_int((buf + INK_MIN_ALIGN), versionObject.get_minor()); |
| } else { |
| marshal_int(buf, 0); |
| marshal_int((buf + INK_MIN_ALIGN), 0); |
| } |
| } |
| return (2 * INK_MIN_ALIGN); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_protocol_version(char *buf) |
| { |
| const char *protocol_str = m_data->get_client_protocol(); |
| std::string_view protocol_sv = m_data->get_client_protocol_str(); |
| if (protocol_str == nullptr || protocol_str[0] == '\0') { |
| if (!protocol_sv.empty()) { |
| protocol_str = protocol_sv.data(); |
| } else { |
| protocol_str = DEFAULT_STR; |
| } |
| } |
| |
| int len = LogAccess::padded_strlen(protocol_str); |
| |
| // Set major & minor versions when protocol_str is not "http/2". |
| if (::strlen(protocol_str) == 4 && strncmp("http", protocol_str, 4) == 0) { |
| if (m_client_request) { |
| HTTPVersion versionObject = m_client_request->version_get(); |
| if (versionObject == HTTP_1_1) { |
| protocol_str = "http/1.1"; |
| } else if (versionObject == HTTP_1_0) { |
| protocol_str = "http/1.0"; |
| } // else invalid http version |
| } else { |
| protocol_str = "*"; |
| } |
| |
| len = LogAccess::padded_strlen(protocol_str); |
| } |
| |
| if (buf) { |
| marshal_str(buf, protocol_str, len); |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_req_protocol_version(char *buf) |
| { |
| const char *server_proto = m_data->get_server_protocol(); |
| const char *protocol_str = server_proto ? server_proto : DEFAULT_STR; |
| int len = LogAccess::padded_strlen(protocol_str); |
| |
| // Set major & minor versions when protocol_str is not "http/2". |
| if (::strlen(protocol_str) == 4 && strncmp("http", protocol_str, 4) == 0) { |
| if (m_proxy_request) { |
| HTTPVersion versionObject = m_proxy_request->version_get(); |
| if (versionObject == HTTP_1_1) { |
| protocol_str = "http/1.1"; |
| } else if (versionObject == HTTP_1_0) { |
| protocol_str = "http/1.0"; |
| } // else invalid http version |
| } else { |
| protocol_str = "*"; |
| } |
| |
| len = LogAccess::padded_strlen(protocol_str); |
| } |
| |
| if (buf) { |
| marshal_str(buf, protocol_str, len); |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_header_len(char *buf) |
| { |
| if (buf) { |
| int64_t len = 0; |
| if (m_client_request) { |
| len = m_client_request->length_get(); |
| } |
| marshal_int(buf, len); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_content_len(char *buf) |
| { |
| if (buf) { |
| int64_t len = 0; |
| if (m_client_request) { |
| len = m_data->get_client_request_body_bytes(); |
| } |
| marshal_int(buf, len); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_squid_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_client_request) { |
| val = m_client_request->length_get() + m_data->get_client_request_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Client request squid length plus TLS handshake bytes received for TLS connections. |
| For TLS 1.3 early data (0-RTT), we subtract the early data length from the |
| handshake bytes to avoid double-counting since the early data bytes are |
| already included in client_request_body_bytes. |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_req_squid_len_tls(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| |
| if (m_client_request) { |
| val = m_client_request->length_get() + m_data->get_client_request_body_bytes(); |
| } |
| |
| if (!m_data->get_client_tcp_reused()) { |
| uint64_t handshake_rx = m_data->get_client_tls_handshake_bytes_rx(); |
| size_t early_data_len = m_data->get_client_tls_early_data_len(); |
| |
| if (early_data_len > 0) { |
| handshake_rx -= std::min(handshake_rx, static_cast<uint64_t>(early_data_len)); |
| } |
| |
| val += handshake_rx; |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_tcp_reused(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_client_tcp_reused() ? 1 : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_is_ssl(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_client_connection_is_ssl() ? 1 : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_ssl_reused(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_client_ssl_reused() ? 1 : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_ssl_resumption_type(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_ssl_resumption_type()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_is_internal(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_is_internal() ? 1 : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_mptcp_state(char *buf) |
| { |
| if (buf) { |
| int val = -1; |
| std::optional<bool> mptcp_state = m_data->get_mptcp_state(); |
| |
| if (mptcp_state.has_value()) { |
| val = mptcp_state.value() ? 1 : 0; |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_finish_status_code(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_finish_status_code()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_sm_id()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_uuid(char *buf) |
| { |
| char str[TS_CRUUID_STRING_LEN + 1]; |
| const char *uuid = Machine::instance()->process_uuid.getString(); |
| int64_t req_id = m_data->get_sm_id(); |
| int len = snprintf(str, sizeof(str), "%s-%" PRId64 "", uuid, req_id); |
| |
| ink_assert(len <= TS_CRUUID_STRING_LEN); |
| len = padded_length(len + 1); |
| |
| if (buf) { |
| marshal_str(buf, str, len); // This will pad the remaining bytes properly ... |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_rx_error_code(char *buf) |
| { |
| const char *err_code = m_data->get_client_rx_error_code(); |
| int round_len = LogAccess::padded_strlen(err_code); |
| |
| if (buf) { |
| marshal_str(buf, err_code, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_tx_error_code(char *buf) |
| { |
| const char *err_code = m_data->get_client_tx_error_code(); |
| int round_len = LogAccess::padded_strlen(err_code); |
| |
| if (buf) { |
| marshal_str(buf, err_code, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_security_protocol(char *buf) |
| { |
| const char *sec_proto = m_data->get_client_sec_protocol(); |
| const char *proto = sec_proto ? sec_proto : DEFAULT_STR; |
| int round_len = LogAccess::padded_strlen(proto); |
| |
| if (buf) { |
| marshal_str(buf, proto, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_cipher_suite(char *buf) |
| { |
| const char *cipher_suite = m_data->get_client_cipher_suite(); |
| const char *cipher = cipher_suite ? cipher_suite : DEFAULT_STR; |
| int round_len = LogAccess::padded_strlen(cipher); |
| |
| if (buf) { |
| marshal_str(buf, cipher, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_curve(char *buf) |
| { |
| const char *client_curve = m_data->get_client_curve(); |
| const char *curve = client_curve ? client_curve : DEFAULT_STR; |
| int round_len = LogAccess::padded_strlen(curve); |
| |
| if (buf) { |
| marshal_str(buf, curve, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_group(char *buf) |
| { |
| const char *sec_group = m_data->get_client_security_group(); |
| const char *group = sec_group ? sec_group : DEFAULT_STR; |
| int round_len = LogAccess::padded_strlen(group); |
| |
| if (buf) { |
| marshal_str(buf, group, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_alpn(char *buf) |
| { |
| const char *alpn = "-"; |
| int alpn_id = m_data->get_client_alpn_id(); |
| if (alpn_id != SessionProtocolNameRegistry::INVALID) { |
| swoc::TextView client_sec_alpn = globalSessionProtocolNameRegistry.nameFor(alpn_id); |
| alpn = client_sec_alpn.data(); |
| } |
| |
| int round_len = LogAccess::padded_strlen(alpn); |
| |
| if (buf) { |
| marshal_str(buf, alpn, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| TLS Handshake Bytes - Bytes received from client during TLS handshake |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_tls_handshake_bytes_rx(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_client_tls_handshake_bytes_rx())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| TLS Handshake Bytes - Bytes sent to client during TLS handshake |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_tls_handshake_bytes_tx(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_client_tls_handshake_bytes_tx())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| TLS Handshake Bytes - Total bytes (rx + tx) during TLS handshake |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_tls_handshake_bytes(char *buf) |
| { |
| if (buf) { |
| uint64_t total = m_data->get_client_tls_handshake_bytes_rx() + m_data->get_client_tls_handshake_bytes_tx(); |
| marshal_int(buf, static_cast<int64_t>(total)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_content_type(char *buf) |
| { |
| int len = padded_length(m_proxy_resp_content_type_len + 1); |
| if (buf) { |
| marshal_mem(buf, m_proxy_resp_content_type_str, m_proxy_resp_content_type_len, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_reason_phrase(char *buf) |
| { |
| int len = padded_length(m_proxy_resp_reason_phrase_len + 1); |
| if (buf) { |
| marshal_mem(buf, m_proxy_resp_reason_phrase_str, m_proxy_resp_reason_phrase_len, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Squid returns the content-length + header length as the total length. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_squid_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_client_response_hdr_bytes() + m_data->get_client_response_body_bytes(); |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Squid length plus TLS handshake bytes sent for TLS connections. |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_resp_squid_len_tls(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_data->get_client_response_hdr_bytes() + m_data->get_client_response_body_bytes(); |
| |
| if (!m_data->get_client_tcp_reused()) { |
| val += m_data->get_client_tls_handshake_bytes_tx(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_content_len(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_response_body_bytes()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_status_code(char *buf) |
| { |
| if (buf) { |
| HTTPStatus status; |
| if (m_proxy_response && m_client_request) { |
| if (m_client_request->version_get() >= HTTPVersion(1, 0)) { |
| status = m_proxy_response->status_get(); |
| } |
| // INKqa10788 |
| // For bad/incomplete request, the request version may be 0.9. |
| // However, we can still log the status code if there is one. |
| else if (m_proxy_response->valid()) { |
| status = m_proxy_response->status_get(); |
| } else { |
| status = HTTPStatus::OK; |
| } |
| } else { |
| status = HTTPStatus::NONE; |
| } |
| marshal_int(buf, static_cast<int64_t>(status)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_status_plugin_entry(char *buf) |
| { |
| char const *str = nullptr; |
| int len = INK_MIN_ALIGN; |
| std::string_view setter_name = m_data->get_http_return_code_setter_name(); |
| |
| if (!setter_name.empty()) { |
| str = setter_name.data(); |
| len = LogAccess::padded_strlen(str); |
| } |
| |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_header_len(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_client_response_hdr_bytes()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_proxy_finish_status_code(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_proxy_finish_status_code()); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_host_port(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_incoming_port())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_result_code(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_log_code())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_result_subcode(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_subcode())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_hit_miss(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_hit_miss_code())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_header_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_proxy_request) { |
| val = m_proxy_request->length_get(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_content_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_proxy_request) { |
| val = m_data->get_server_request_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_proxy_req_squid_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_proxy_request) { |
| val = m_proxy_request->length_get() + m_data->get_server_request_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| // TODO: Change marshalling code to support both IPv4 and IPv6 addresses. |
| int |
| LogAccess::marshal_proxy_req_server_ip(char *buf) |
| { |
| return marshal_ip(buf, m_data->get_server_src_addr()); |
| } |
| |
| int |
| LogAccess::marshal_proxy_req_server_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = 0; |
| sockaddr const *src = m_data->get_server_src_addr(); |
| if (src) { |
| port = ats_ip_port_host_order(src); |
| } |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_next_hop_ip(char *buf) |
| { |
| return marshal_ip(buf, m_data->get_server_dst_addr()); |
| } |
| |
| int |
| LogAccess::marshal_next_hop_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = 0; |
| sockaddr const *dst = m_data->get_server_dst_addr(); |
| if (dst) { |
| port = ats_ip_port_host_order(dst); |
| } |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_is_ssl(char *buf) |
| { |
| if (buf) { |
| int64_t is_ssl = m_data->get_server_connection_is_ssl() ? 1 : 0; |
| marshal_int(buf, is_ssl); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_proxy_req_ssl_reused(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_server_ssl_reused() ? 1 : 0); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_hierarchy_route(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_data->get_hier_code())); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| // TODO: Change marshalling code to support both IPv4 and IPv6 addresses. |
| int |
| LogAccess::marshal_server_host_ip(char *buf) |
| { |
| sockaddr const *ip = nullptr; |
| sockaddr const *info_dst = m_data->get_server_info_dst_addr(); |
| sockaddr const *server_dst = m_data->get_server_dst_addr(); |
| |
| if (info_dst && ats_is_ip(info_dst)) { |
| ip = info_dst; |
| } else if (server_dst && ats_is_ip(server_dst)) { |
| ip = server_dst; |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_host_name(char *buf) |
| { |
| const char *str = nullptr; |
| int len = INK_MIN_ALIGN; |
| sockaddr const *server_dst = m_data->get_server_dst_addr(); |
| const char *name = m_data->get_server_name(); |
| |
| if (server_dst && name != nullptr) { |
| str = name; |
| len = LogAccess::padded_strlen(str); |
| } |
| |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_resp_status_code(char *buf) |
| { |
| if (buf) { |
| HTTPStatus status; |
| if (m_server_response) { |
| status = m_server_response->status_get(); |
| } else { |
| status = HTTPStatus::NONE; |
| } |
| marshal_int(buf, static_cast<int64_t>(status)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_resp_content_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_server_response) { |
| val = m_data->get_server_response_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_resp_header_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_server_response) { |
| val = m_server_response->length_get(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_server_resp_squid_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_server_response) { |
| val = m_server_response->length_get() + m_data->get_server_response_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_server_resp_http_version(char *buf) |
| { |
| return marshal_http_version_string(buf, m_server_response); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_server_resp_time_ms(char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *ms = m_data->get_milestones(); |
| int64_t val = ms ? ms->difference_msec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_server_resp_time_s(char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *ms = m_data->get_milestones(); |
| int64_t val = ms ? static_cast<int64_t>(ms->difference_sec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE)) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_transact_count(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_server_transact_count()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_simple_retry_count(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_simple_retry_attempts()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_unavailable_retry_count(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_unavailable_retry_attempts()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_connect_attempts(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_retry_attempts_saved()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_resp_status_code(char *buf) |
| { |
| if (buf) { |
| HTTPStatus status; |
| if (m_cache_response) { |
| status = m_cache_response->status_get(); |
| } else { |
| status = HTTPStatus::NONE; |
| } |
| marshal_int(buf, static_cast<int64_t>(status)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_resp_content_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_cache_response) { |
| val = m_data->get_cache_response_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_resp_squid_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_cache_response) { |
| val = m_cache_response->length_get() + m_data->get_cache_response_body_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_resp_header_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = 0; |
| if (m_cache_response) { |
| val = m_data->get_cache_response_hdr_bytes(); |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_resp_http_version(char *buf) |
| { |
| return marshal_http_version_string(buf, m_cache_response); |
| } |
| |
| int |
| LogAccess::marshal_client_retry_after_time(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_congestion_control_crat()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_write_code(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_cache_write_code()); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_write_transform_code(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_cache_transform_write_code()); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_key_hash(char *buf) |
| { |
| const ts::CryptoHash *hash = m_data->get_cache_lookup_hash(); |
| |
| if (!hash || hash->is_zero()) { |
| if (buf) { |
| marshal_str(buf, "-", padded_length(2)); |
| } |
| return padded_length(2); |
| } |
| |
| constexpr size_t b64_bufsize = ats_base64_encode_dstlen(CRYPTO_HASH_SIZE); |
| char b64_str[b64_bufsize]; |
| size_t b64_len = 0; |
| |
| ats_base64_encode(reinterpret_cast<const char *>(hash->u8), CRYPTO_HASH_SIZE, b64_str, b64_bufsize, &b64_len); |
| |
| int len = padded_length(b64_len + 1); |
| |
| if (buf) { |
| marshal_str(buf, b64_str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_transfer_time_ms(char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *ms = m_data->get_milestones(); |
| int64_t val = ms ? ms->difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_transfer_time_s(char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *ms = m_data->get_milestones(); |
| int64_t val = ms ? static_cast<int64_t>(ms->difference_sec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH)) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Figure out the size of the object *on origin*. This is somewhat tricky |
| since there are many variations on how this can be calculated. |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_file_size(char *buf) |
| { |
| if (buf) { |
| MIMEField *fld; |
| HTTPHdr *hdr = m_server_response ? m_server_response : m_cache_response; |
| |
| if (hdr && (fld = hdr->field_find(static_cast<std::string_view>(MIME_FIELD_CONTENT_RANGE)))) { |
| auto value{fld->value_get()}; |
| int len = value.length(); |
| char *str = const_cast<char *>(value.data()); |
| char *pos = static_cast<char *>(memchr(str, '/', len)); // Find the / |
| |
| // If the size is not /* (which means unknown) use it as the file_size. |
| if (pos && !memchr(pos + 1, '*', len - (pos + 1 - str))) { |
| marshal_int(buf, ink_atoi64(pos + 1, len - (pos + 1 - str))); |
| } |
| } else { |
| // This is semi-broken when we serveq zero length objects. See TS-2213 |
| int64_t server_body = m_data->get_server_response_body_bytes(); |
| int64_t cache_body = m_data->get_cache_response_body_bytes(); |
| if (server_body > 0) { |
| marshal_int(buf, server_body); |
| } else if (cache_body > 0) { |
| marshal_int(buf, cache_body); |
| } |
| } |
| } |
| // Else, we don't set the value at all (so, -) |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_connection_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_connection_id()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_transaction_id()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_priority_weight(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_transaction_priority_weight()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_priority_dependence(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_transaction_priority_dependence()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_read_retries(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_cache_open_read_tries()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_write_retries(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_data->get_cache_open_write_tries()); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_collapsed_connection_success(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; // default - no collapse attempt |
| SquidLogCode code = m_data->get_log_code(); |
| int open_write_tries = m_data->get_cache_open_write_tries(); |
| int max_write_retries = m_data->get_max_cache_open_write_retries(); |
| |
| if ((open_write_tries > 0) && ((code == SquidLogCode::TCP_HIT) || (code == SquidLogCode::TCP_MEM_HIT) || |
| (code == SquidLogCode::TCP_DISK_HIT) || (code == SquidLogCode::TCP_CF_HIT))) { |
| id = 1; |
| } else if (max_write_retries >= 0 && open_write_tries > max_write_retries) { |
| id = -1; |
| } |
| |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_http_header_field(LogField::Container container, char *field, char *buf) |
| { |
| char *str = nullptr; |
| int padded_len = INK_MIN_ALIGN; |
| int actual_len = 0; |
| bool valid_field = false; |
| HTTPHdr *header = header_for_container(container); |
| |
| if (header) { |
| MIMEField *fld = header->field_find(std::string_view{field}); |
| if (fld) { |
| valid_field = true; |
| |
| // Loop over dups, marshalling each one into the buffer and |
| // summing up their length |
| // |
| int running_len = 0; |
| while (fld) { |
| auto value{fld->value_get()}; |
| actual_len = value.length(); |
| str = const_cast<char *>(value.data()); |
| if (buf) { |
| memcpy(buf, str, actual_len); |
| buf += actual_len; |
| } |
| running_len += actual_len; |
| fld = fld->m_next_dup; |
| |
| // Dups need to be comma separated. So if there's another |
| // dup, then add a comma and a space ... |
| // |
| if (fld != nullptr) { |
| if (buf) { |
| memcpy(buf, ", ", 2); |
| buf += 2; |
| } |
| running_len += 2; |
| } |
| } |
| |
| // Done with all dups. Ensure that the string is terminated |
| // and that the running_len is padded. |
| // |
| if (buf) { |
| *buf = '\0'; |
| buf++; |
| } |
| running_len += 1; |
| padded_len = padded_length(running_len); |
| |
| // Note: marshal_string fills the padding to |
| // prevent purify UMRs so we do it here too |
| // since we always pass the unpadded length on |
| // our calls to marshal string |
| #ifdef DEBUG |
| if (buf) { |
| int pad_len = padded_len - running_len; |
| for (int i = 0; i < pad_len; i++) { |
| *buf = '$'; |
| buf++; |
| } |
| } |
| #endif |
| } |
| } |
| |
| // The Transfer-Encoding:chunked value may have been removed from the header |
| // during processing, but we store it for logging purposes. |
| std::string_view transfer_enc = m_data->get_server_response_transfer_encoding(); |
| if (valid_field == false && !transfer_enc.empty() && container == LogField::SSH && strcmp(field, "Transfer-Encoding") == 0) { |
| std::string_view stored_te = transfer_enc; |
| if (!stored_te.empty()) { |
| valid_field = true; |
| actual_len = stored_te.length(); |
| str = const_cast<char *>(stored_te.data()); |
| |
| int running_len = actual_len + 1; // +1 for null terminator |
| padded_len = padded_length(running_len); |
| |
| if (buf) { |
| memcpy(buf, str, actual_len); |
| buf[actual_len] = '\0'; |
| buf += running_len; |
| |
| #ifdef DEBUG |
| int pad_len = padded_len - running_len; |
| for (int i = 0; i < pad_len; i++) { |
| *buf = '$'; |
| buf++; |
| } |
| #endif |
| } |
| } |
| } |
| |
| if (valid_field == false) { |
| padded_len = INK_MIN_ALIGN; |
| if (buf) { |
| marshal_str(buf, nullptr, padded_len); |
| } |
| } |
| |
| return (padded_len); |
| } |
| |
| int |
| LogAccess::marshal_http_header_field_escapify(LogField::Container container, char *field, char *buf) |
| { |
| char *str = nullptr, *new_str = nullptr; |
| int padded_len = INK_MIN_ALIGN; |
| int actual_len = 0, new_len = 0; |
| bool valid_field = false; |
| HTTPHdr *header = header_for_container(container); |
| |
| if (header) { |
| MIMEField *fld = header->field_find(std::string_view{field}); |
| if (fld) { |
| valid_field = true; |
| |
| // Loop over dups, marshalling each one into the buffer and |
| // summing up their length |
| // |
| int running_len = 0; |
| while (fld) { |
| auto value{fld->value_get()}; |
| actual_len = value.length(); |
| str = const_cast<char *>(value.data()); |
| new_str = Encoding::escapify_url(&m_arena, str, actual_len, &new_len); |
| if (buf) { |
| memcpy(buf, new_str, new_len); |
| buf += new_len; |
| } |
| running_len += new_len; |
| fld = fld->m_next_dup; |
| |
| // Dups need to be comma separated. So if there's another |
| // dup, then add a comma and an escapified space ... |
| constexpr const char SEP[] = ",%20"; |
| constexpr size_t SEP_LEN = sizeof(SEP) - 1; |
| if (fld != nullptr) { |
| if (buf) { |
| memcpy(buf, SEP, SEP_LEN); |
| buf += SEP_LEN; |
| } |
| running_len += SEP_LEN; |
| } |
| } |
| |
| // Done with all dups. Ensure that the string is terminated |
| // and that the running_len is padded. |
| // |
| if (buf) { |
| *buf = '\0'; |
| buf++; |
| } |
| running_len += 1; |
| padded_len = padded_length(running_len); |
| |
| // Note: marshal_string fills the padding to |
| // prevent purify UMRs so we do it here too |
| // since we always pass the unpadded length on |
| // our calls to marshal string |
| #ifdef DEBUG |
| if (buf) { |
| int pad_len = padded_len - running_len; |
| for (int i = 0; i < pad_len; i++) { |
| *buf = '$'; |
| buf++; |
| } |
| } |
| #endif |
| } |
| } |
| |
| if (valid_field == false) { |
| padded_len = INK_MIN_ALIGN; |
| if (buf) { |
| marshal_str(buf, nullptr, padded_len); |
| } |
| } |
| |
| return (padded_len); |
| } |
| |
| int |
| LogAccess::marshal_milestone(TSMilestonesType ms, char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *milestones = m_data->get_milestones(); |
| int64_t val = milestones ? ink_hrtime_to_msec((*milestones)[ms]) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_fmt_sec(TSMilestonesType type, char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *milestones = m_data->get_milestones(); |
| ink_hrtime tsec = milestones ? ink_hrtime_to_sec((*milestones)[type]) : 0; |
| marshal_int(buf, tsec); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_fmt_ms(TSMilestonesType type, char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *milestones = m_data->get_milestones(); |
| ink_hrtime tmsec = milestones ? ink_hrtime_to_msec((*milestones)[type]) : 0; |
| marshal_int(buf, tmsec); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_diff(TSMilestonesType ms1, TSMilestonesType ms2, char *buf) |
| { |
| if (buf) { |
| TransactionMilestones const *milestones = m_data->get_milestones(); |
| int64_t val = milestones ? milestones->difference_msec(ms2, ms1) : 0; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestones_csv(char *buf) |
| { |
| Dbg(dbg_ctl_log_unmarshal_data, "marshal_milestones_csv"); |
| |
| TransactionMilestones const *ms_ptr = m_data->get_milestones(); |
| if (!ms_ptr) { |
| int len = LogAccess::padded_strlen("-"); |
| if (buf) { |
| marshal_str(buf, "-", len); |
| } |
| return len; |
| } |
| auto const &milestones = *ms_ptr; |
| swoc::LocalBufferWriter<256> bw; |
| |
| bw.print("{}", milestones.difference_msec(TS_MILESTONE_TLS_HANDSHAKE_START, TS_MILESTONE_TLS_HANDSHAKE_END)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_FIRST_READ)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_READ_HEADER_DONE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_BEGIN)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_END)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_END)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_BEGIN)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_END)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT_END)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_FIRST_READ)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_READ_HEADER_DONE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CLOSE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN_WRITE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_CLOSE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_PLUGIN_ACTIVE)); |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_PLUGIN_TOTAL)); |
| bw.print("\0"); |
| |
| auto const view = bw.view(); |
| int const len = LogAccess::padded_strlen(view.data()); |
| |
| if (nullptr != buf) { |
| marshal_str(buf, view.data(), len); |
| } |
| return len; |
| } |
| |
| void |
| LogAccess::set_http_header_field(LogField::Container container, char *field, char *buf, int len) |
| { |
| HTTPHdr *header; |
| |
| switch (container) { |
| case LogField::CQH: |
| case LogField::ECQH: |
| header = m_client_request; |
| break; |
| |
| case LogField::PSH: |
| case LogField::EPSH: |
| header = m_proxy_response; |
| break; |
| |
| case LogField::PQH: |
| case LogField::EPQH: |
| header = m_proxy_request; |
| break; |
| |
| case LogField::SSH: |
| case LogField::ESSH: |
| header = m_server_response; |
| break; |
| |
| case LogField::CSSH: |
| case LogField::ECSSH: |
| header = m_cache_response; |
| break; |
| |
| default: |
| header = nullptr; |
| break; |
| } |
| |
| if (header && buf) { |
| MIMEField *fld = header->field_find(std::string_view{field}); |
| if (fld) { |
| // Loop over dups, update each of them |
| // |
| while (fld) { |
| // make sure to reuse header heaps as otherwise |
| // coalesce logic in header heap may free up |
| // memory pointed to by cquuc or other log fields |
| header->field_value_set(fld, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); |
| fld = fld->m_next_dup; |
| } |
| } |
| } |
| } |