blob: f7b2ab6a10f6e666654c56dab86e1ab7bc785516 [file]
/*
* 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 <ts/ts.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cstdlib>
#include <memory>
#include <string_view>
#include <string>
namespace
{
std::string_view PNAME = "test_plugin";
DbgCtl dbg_ctl{PNAME.data()};
/** Represents the origin address and port that the user specified to which to
* redirect requests. */
class TargetAddress
{
public:
/** Constructs a TargetAddress object from the given address and port.
*
* @param address The address to which to redirect requests.
* @param port The port to which to redirect requests.
*/
TargetAddress(char const *address, int port) : _address(address), _port(port)
{
_sockaddr.sin_family = AF_INET;
_sockaddr.sin_port = htons(port);
if (inet_pton(AF_INET, address, &_sockaddr.sin_addr) != 1) {
TSError("Invalid address %s", address);
_is_valid = false;
} else {
_is_valid = true;
}
}
/** Returns the address to which to redirect requests.
*
* @return The address to which to redirect requests.
*/
std::string_view
get_address() const
{
return _address;
}
/** Returns the port to which to redirect requests.
*
* @return The port to which to redirect requests.
*/
int
get_port() const
{
return _port;
}
/** Returns whether the address and port are valid.
*
* @return Whether the address and port are valid.
*/
bool
is_valid() const
{
return _is_valid;
}
/** Returns the sockaddr representing the user's specified origin address and
* port.
*
* @return The sockaddr representing the user's specified origin address and
* port.
*/
sockaddr const *
get_sockaddr() const
{
return reinterpret_cast<sockaddr const *>(&_sockaddr);
}
private:
std::string _address;
int _port;
sockaddr_in _sockaddr;
bool _is_valid;
};
/** The user specified origin to which requests are redirected. */
std::unique_ptr<TargetAddress> g_target_address{nullptr};
/** Parse the plugin's command line arguments. */
bool
parse_arguments(int argc, char const *argv[])
{
if (argc != 3) {
TSError("Must provide the address and port for TSHttpTxnServerAddrSet.");
return false;
}
char const *address = argv[1];
int port = atoi(argv[2]);
if (port <= 0 || port >= 65536) {
TSError("Invalid port number %s", argv[2]);
return false;
}
g_target_address = std::make_unique<TargetAddress>(address, port);
if (!g_target_address->is_valid()) {
TSError("Invalid address %s:%d", address, port);
return false;
}
return true;
}
/** The handler which sets the user-specified origin. */
int
set_origin(TSCont /* cont ATS_UNUSED */, TSEvent event, void *edata)
{
if (event != TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE) {
TSError("Unexpected event: %d", event);
return TS_ERROR;
}
TSHttpTxn txnp = (TSHttpTxn)edata;
if (TSHttpTxnServerAddrSet(txnp, g_target_address->get_sockaddr()) != TS_SUCCESS) {
TSError("Failed to set a transaction's origin to %s:%d", g_target_address->get_address().data(), g_target_address->get_port());
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
return TS_ERROR;
}
Dbg(dbg_ctl, "Successfully set a transaction's origin to %s:%d", g_target_address->get_address().data(),
g_target_address->get_port());
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
return TS_SUCCESS;
}
} // anonymous namespace
void
TSPluginInit(int argc, char const *argv[])
{
Dbg(dbg_ctl, "Initializing plugin.");
TSPluginRegistrationInfo info;
info.plugin_name = PNAME.data();
info.vendor_name = "Apache Software Foundation";
info.support_email = "dev@trafficserver.apache.org";
TSReleaseAssert(TSPluginRegister(&info) == TS_SUCCESS);
if (!parse_arguments(argc, argv)) {
TSError("Failed to parse arguments.");
return;
}
Dbg(dbg_ctl, "Redirecting all requests to %s:%s", argv[1], argv[2]);
TSCont contp = TSContCreate(set_origin, nullptr);
TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
}