Github user SolidWallOfCode commented on a diff in the pull request:
https://github.com/apache/trafficserver/pull/843#discussion_r75859721
--- Diff: plugins/experimental/carp/carp.cc ---
@@ -0,0 +1,713 @@
+/** @file
+
+ A brief file description
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include <errno.h>
+
+#include <string>
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory.h>
+
+#include <ts/ts.h>
+
+#include "Common.h"
+#include "CarpConfig.h"
+#include "CarpConfigPool.h"
+#include "CarpHashAlgorithm.h"
+#include "UrlComponents.h"
+
+using namespace std;
+
+CarpConfigPool* g_CarpConfigPool = NULL;
+int g_carpSelectedHostArgIndex = 0;
+TSTextLogObject g_logObject = NULL;
+
+const char *logFileName = "carp";
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+/*
+ check for our carp routed header, dump status if requested
+ */
+static int
+processCarpRoutedHeader(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc)
+{
+ string value;
+ if (getHeader(bufp, hdr_loc, CARP_ROUTED_HEADER, value)) { // if found
header
+ if (value.compare("1") == 0) { // is loop prevention value
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with loop prevention value,
not forwarding again", CARP_ROUTED_HEADER.c_str());
+ return 0;
+ } else if (value.compare("dump") == 0) { // is dump status request
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with dump request",
CARP_ROUTED_HEADER.c_str());
+ string status;
+ g_CarpConfigPool->getGlobalHashAlgo()->dump(status);
+ TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_MULTI_STATUS);
+ TSHttpTxnErrorBodySet(txnp, TSstrdup(status.c_str()),
status.length(), NULL);
+ return -1;
+ }
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with unknown value of %s,
ignoring", CARP_ROUTED_HEADER.c_str(), value.c_str());
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ }
+ return 1; // all OK
+}
+
+static bool
+checkListForSelf(std::vector<HashNode *> list)
+{
+ for (size_t k = 0; k < list.size(); k++) {
+ if (list[k]->isSelf) return true;
+ }
+ return false;
+}
+
+/**
+ bIsPOSTRemap = false --- Hash request and forward to peer
+ bIsPOSTRemap = true --- hash request, extract OS sockaddr, insert
forwarding header, forward
+ */
+static int
+handleRequestProcessing(TSCont contp, TSEvent event, void *edata, bool
bIsPOSTRemap)
+{
+ TSHttpTxn txnp = (TSHttpTxn) edata;
+ TSMBuffer bufp;
+ TSMLoc hdr_loc;
+ TSMLoc url_loc;
+
+ // get the client request so we can get URL and add header
+ if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+ TSError("carp couldn't get request headers");
+ return -1;
+ }
+
+ int method_len;
+ const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
+ if (NULL == method) {
+ TSError("carp couldn't get http method");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return -1;
+ }
+ if (((method_len == TS_HTTP_LEN_DELETE) && (strncasecmp(method,
TS_HTTP_METHOD_DELETE, TS_HTTP_LEN_DELETE) == 0)) ||
+ ((method_len == TS_HTTP_LEN_PURGE) && (strncasecmp(method,
TS_HTTP_METHOD_PURGE, TS_HTTP_LEN_PURGE) == 0))) {
+ TSDebug(DEBUG_TAG_HOOK, "Request method is '%s' so not routing
request", string(method,method_len).c_str());
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
+ TSError("carp couldn't get url");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return -1;
+ }
+ // if has carp loop prevention header, do not remap
+ int iTemp = processCarpRoutedHeader(txnp, bufp, hdr_loc);
+ if(iTemp <= 0) { // if dump or do not remap
+ // check origin client request's scheme for premap mode
+ if (!bIsPOSTRemap) {
+ string oriScheme;
+ if (!getHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME, oriScheme)) {
+ TSDebug(DEBUG_TAG_HOOK, "couldn't get '%s' header",
CARP_PREMAP_SCHEME.c_str());
+ } else {
+ bool isHttps = (oriScheme == TS_URL_SCHEME_HTTPS);
+
+ if (isHttps) {
+ TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTPS,
TS_URL_LEN_HTTPS);
+ } else {
+ TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTP,
TS_URL_LEN_HTTP);
+ }
+
+ removeHeader(bufp, hdr_loc, CARP_STATUS_HEADER);
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ removeHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME.c_str());
+ TSDebug(DEBUG_TAG_HOOK, "Set client request's scheme to %s through
%s header",
+ isHttps?"https":"http", CARP_PREMAP_SCHEME.c_str());
+ }
+ } else {
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ }
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return iTemp;
+ }
+
+ UrlComponents reqUrl;
+ reqUrl.populate(bufp, url_loc);
+ // the url ONLY used to determine the cache owner
+ string sUrl;
+
+ if (!bIsPOSTRemap) { // if pre-remap, then host not in URL so get from
header
+ string sHost;
+ if (!getHeader(bufp, hdr_loc, TS_MIME_FIELD_HOST, sHost)) {
+ TSDebug(DEBUG_TAG_HOOK, "Could not find host header, ignoring it");
+ }
+ reqUrl.setHost(sHost);
+
+ //[YTSATS-836] heuristically ignore the scheme and port when calculate
cache owner
+ UrlComponents normalizedUrl = reqUrl;
+ normalizedUrl.setScheme(CARP_SCHEME_FOR_HASH);
+ normalizedUrl.setPort(CARP_PORT_FOR_HASH);
+ normalizedUrl.construct(sUrl);
+ } else {
+ reqUrl.construct(sUrl);
+ }
+
+
+ if (g_CarpConfigPool->getGlobalConfig()->hasWhiteList()) {
+ string sCarpable;
+ if (!getHeader(bufp, hdr_loc, CARPABLE_HEADER, sCarpable)) { // if no
carpable header check whitelist
+ if
(!g_CarpConfigPool->getGlobalConfig()->isWhiteListed(reqUrl.getHost())) { // if
white list exists, then host must be present
+ TSDebug(DEBUG_TAG_HOOK, "Host '%s' is not whitelisted, not going
through carp", reqUrl.getHost().c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ } else {
+ TSDebug(DEBUG_TAG_HOOK, "Found host (%s) whitelisted,
routing...",reqUrl.getHost().c_str());
+ }
+ } else { // found carpable header, make sure it's 1
+ if (sCarpable.compare("1") != 0) { // carpable header present but
not 0, be strict and do not forward request
+ TSDebug(DEBUG_TAG_HOOK, "Carpable (%s) present but value not
acceptable (%s)", CARPABLE_HEADER.c_str(), sCarpable.c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ } else {
+ TSDebug(DEBUG_TAG_HOOK, "Found Carpable header, routing...");
+ }
+ }
+ } else { // no whitelist so blacklist could be used
+ if
(g_CarpConfigPool->getGlobalConfig()->isBlackListed(reqUrl.getHost())) { // if
host black listed, do not carp
+ TSDebug(DEBUG_TAG_HOOK, "Host '%s' is blacklisted, not going through
carp", reqUrl.getHost().c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+ }
+
+ TSDebug(DEBUG_TAG_HOOK, "URL to hash with=%s", sUrl.c_str());
+
+ // get nodeList and select node to forward to
+ std::vector<HashNode *> nodeList =
g_CarpConfigPool->getGlobalHashAlgo()->getRemapProxyList(sUrl);
+ bool bAddHeaderResult=false;
+ bool bAddForwardedResult = false;
+ HashNode* node = NULL;
+ bool bIsOwner = false;
+
+ if (nodeList.size() == 0) { // no hosts available to forward to
+ TSDebug(DEBUG_TAG_HOOK, "no hosts available to forward to, will handle
locally");
+ goto done;
+ } else {
+ node = nodeList[0];
+ }
+
+ bIsOwner = checkListForSelf(nodeList);
+ for (size_t k = 0; k < nodeList.size(); k++) {
+ TSDebug(DEBUG_TAG_HOOK, "nodeList host %d name is %s",
static_cast<int>(k), nodeList[k]->name.c_str());
+ }
+
+ //handle forwarding
+ TSDebug(DEBUG_TAG_HOOK, "forwarding to '%s' (isSelf=%d)",
node->name.c_str(), node->isSelf);
+ if (!node->isSelf) { // carp does not forward if we choose ourself
+ node->carpForward();
+ TSDebug(DEBUG_TAG_HOOK, "carp forwarded to %s.", node->name.c_str());
+ // insert carp loop prevention header
+ bAddHeaderResult = addHeader(bufp, hdr_loc, CARP_ROUTED_HEADER,
string("1"));
+ if (!bAddHeaderResult) {
+ TSError("Carp, error inserting '%s' header",
CARP_ROUTED_HEADER.c_str());
+ }
+ bAddForwardedResult = addHeader(bufp, hdr_loc, CARP_STATUS_HEADER,
string(CARP_FORWARDED));
+ if (!bAddForwardedResult) {
+ TSError("Carp, error inserting '%s' header",
CARP_STATUS_HEADER.c_str());
+ }
+
+ if (bIsPOSTRemap) { // if post remap, get remapped/OS Server Addr and
add as header
+ const struct sockaddr* sa = TSHttpTxnServerAddrGet(txnp);
+ // const struct sockaddr* sa = TSHttpTxnNextHopAddrGet(txnp);
+ if (sa) { // sanity check
+ struct sockaddr_storage ss;
+ memcpy(static_cast<void *> (&ss), sa, sizeof (sockaddr_storage));
+ if ((reinterpret_cast<sockaddr_in *> (&ss))->sin_port == 0) { //
set port from client request URL
+ (reinterpret_cast<sockaddr_in *> (&ss))->sin_port =
htons(reqUrl.getPort());
+ }
+
+ string sTemp;
+ getStringFromSockaddr(reinterpret_cast<const sockaddr *> (&ss),
sTemp);
--- End diff --
This is so messed up I don't know where to start.
* As noted above, `sa` is certain to not be as long as a `sockaddr_storage`
this prints out effectively random bytes to be shipped to the peer, presuming
it doesn't just ASAN crash first.
* Since in other places it is clear only IPv4 is supported, why not use
`sockaddr_in`? What's the benefit of using `soockaddr_storage`?
* Why bother with `ss` at all? AFAICT it's used only to set the port and
since it's just printed to the string anyway, why not just print the port
explicitly?
* Why not use `sTemp`? Or have the debug message use `val`. Having two
different string conversions just for a debug output seems overly complex.
* In preference to either, why not use `inet_ntoa` or `inet_ntop`?
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---