blob: 4f27e29cc6d6e160a38194ff9eb8723c09c6f683 [file] [log] [blame]
/** @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.
*/
/*
* redirect-1.c:
* an example program which redirects clients based on the source IP
*
*
* Usage:
* (NT): Redirect.dll block_ip url_redirect
* (Solaris): redirect-1.so block_ip url_redirect
*
*
*/
#include <string.h>
#if !defined (_WIN32)
# include <unistd.h>
# include <netinet/in.h>
# include <arpa/inet.h>
#else
# include <windows.h>
#endif
#include <ts/ts.h>
#if !defined (_WIN32)
static in_addr_t ip_deny;
#else
static unsigned int ip_deny;
#endif
/*
* uncoupled statistics variables:
*/
static INKStat method_count_redirected_connect;
static INKStat method_count_redirected_delete;
static INKStat method_count_redirected_get;
static INKStat method_count_redirected_head;
static INKStat method_count_redirected_icp_query;
static INKStat method_count_redirected_options;
static INKStat method_count_redirected_post;
static INKStat method_count_redirected_purge;
static INKStat method_count_redirected_put;
static INKStat method_count_redirected_trace;
static INKStat method_count_redirected_unknown;
/*
* coupled statistics variables:
* coupled stat category for the following stats
* is request_outcomes. The relationship among the stats is:
* requests_all = requests_redirects + requests_unchanged
*/
static INKCoupledStat request_outcomes;
static INKStat requests_all;
static INKStat requests_redirects;
static INKStat requests_unchanged;
void update_redirected_method_stats(INKMBuffer bufp, INKMLoc hdr_loc);
static char *url_redirect;
static char *uri_redirect;
static char *block_ip;
static void
handle_client_lookup(INKHttpTxn txnp, INKCont contp)
{
INKMBuffer bufp;
INKMLoc hdr_loc, url_loc;
int host_length;
#if !defined (_WIN32)
in_addr_t clientip;
#else
unsigned int clientip;
#endif
const char *host;
char *clientstring;
struct in_addr tempstruct;
/*
* Here we declare local coupled statistics variables:
*/
INKCoupledStat local_request_outcomes;
INKStat local_requests_all;
INKStat local_requests_redirects;
INKStat local_requests_unchanged;
/*
* Create local copy of the global coupled stat category:
*/
local_request_outcomes = INKStatCoupledLocalCopyCreate("local_request_outcomes", request_outcomes);
/*
* Create the local copies of the global coupled stats:
*/
local_requests_all = INKStatCoupledLocalAdd(local_request_outcomes, "requests.all.local", INKSTAT_TYPE_FLOAT);
local_requests_redirects = INKStatCoupledLocalAdd(local_request_outcomes,
"requests.redirects.local", INKSTAT_TYPE_INT64);
local_requests_unchanged = INKStatCoupledLocalAdd(local_request_outcomes,
"requests.unchanged.local", INKSTAT_TYPE_INT64);
/*
* Increment the count of total requests:
* (it is more natural to treat the number of requests as an
* integer, but we declare this a FLOAT in order to demonstrate
* how to increment coupled FLOAT stats)
*/
INKStatFloatAddTo(local_requests_all, 1.0);
#if !defined (_WIN32)
clientip = (in_addr_t) INKHttpTxnClientIPGet(txnp);
#else
clientip = INKHttpTxnClientIPGet(txnp);
#endif
tempstruct.s_addr = clientip;
clientstring = inet_ntoa(tempstruct);
INKDebug("redirect", "clientip is %s and block_ip is %s", clientstring, block_ip);
if (!INKHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
INKError("couldn't retrieve client request header\n");
goto done;
}
url_loc = INKHttpHdrUrlGet(bufp, hdr_loc);
if (!url_loc) {
INKError("couldn't retrieve request url\n");
INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
goto done;
}
host = INKUrlHostGet(bufp, url_loc, &host_length);
if (!host) {
INKError("couldn't retrieve request hostname\n");
INKHandleMLocRelease(bufp, hdr_loc, url_loc);
INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
goto done;
}
/*
* Check to see if the client is already headed to the redirect site.
*/
if (strncmp(host, url_redirect, host_length) == 0) {
INKHandleStringRelease(bufp, url_loc, host);
INKHandleMLocRelease(bufp, hdr_loc, url_loc);
INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
goto done;
}
if (ip_deny == clientip) {
INKHttpTxnHookAdd(txnp, INK_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
INKHandleStringRelease(bufp, url_loc, host);
update_redirected_method_stats(bufp, hdr_loc);
INKHandleMLocRelease(bufp, hdr_loc, url_loc);
INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
/*
* Increment the local redirect stat and do global update:
*/
INKStatIncrement(local_requests_redirects);
INKStatsCoupledUpdate(local_request_outcomes);
INKStatCoupledLocalCopyDestroy(local_request_outcomes);
INKHttpTxnReenable(txnp, INK_EVENT_HTTP_ERROR);
return;
}
done:
/*
* Increment the local number unchanged stat and do global update:
*/
INKStatIncrement(local_requests_unchanged);
INKStatsCoupledUpdate(local_request_outcomes);
INKStatCoupledLocalCopyDestroy(local_request_outcomes);
INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
}
static void
handle_response(INKHttpTxn txnp)
{
INKMBuffer bufp;
INKMLoc hdr_loc, newfield_loc;
INKMLoc url_loc;
char *url_str;
char *buf;
char *errormsg_body = "All requests from this IP address are redirected.\n";
char *tmp_body;
if (!INKHttpTxnClientRespGet(txnp, &bufp, &hdr_loc)) {
INKError("couldn't retrieve client response header\n");
goto done;
}
INKHttpHdrStatusSet(bufp, hdr_loc, INK_HTTP_STATUS_MOVED_PERMANENTLY);
INKHttpHdrReasonSet(bufp, hdr_loc,
INKHttpHdrReasonLookup(INK_HTTP_STATUS_MOVED_PERMANENTLY),
strlen(INKHttpHdrReasonLookup(INK_HTTP_STATUS_MOVED_PERMANENTLY)));
newfield_loc = INKMimeHdrFieldCreate(bufp, hdr_loc);
INKMimeHdrFieldNameSet(bufp, hdr_loc, newfield_loc, INK_MIME_FIELD_LOCATION, INK_MIME_LEN_LOCATION);
INKMimeHdrFieldValueInsert(bufp, hdr_loc, newfield_loc, uri_redirect, strlen(uri_redirect), -1);
INKMimeHdrFieldInsert(bufp, hdr_loc, newfield_loc, -1);
/*
* Note that we can't directly use errormsg_body, as INKHttpTxnErrorBodySet()
* will try to free the passed buffer with INKfree().
*/
tmp_body = INKstrdup(errormsg_body);
INKHttpTxnErrorBodySet(txnp, tmp_body, strlen(tmp_body), NULL);
INKHandleMLocRelease(bufp, hdr_loc, newfield_loc);
INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
done:
INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
}
static int
redirect_plugin(INKCont contp, INKEvent event, void *edata)
{
INKHttpTxn txnp = (INKHttpTxn) edata;
switch (event) {
case INK_EVENT_HTTP_READ_REQUEST_HDR:
handle_client_lookup(txnp, contp);
return 0;
case INK_EVENT_HTTP_SEND_RESPONSE_HDR:
handle_response(txnp);
return 0;
default:
break;
}
return 0;
}
/*
* Global statistics functions:
*/
void
init_stats(void)
{
/* noncoupled: */
method_count_redirected_connect = INKStatCreate("method.count.redirected.connect", INKSTAT_TYPE_INT64);
method_count_redirected_delete = INKStatCreate("method.count.redirected.delete", INKSTAT_TYPE_INT64);
method_count_redirected_get = INKStatCreate("method.count.redirected.get", INKSTAT_TYPE_INT64);
method_count_redirected_head = INKStatCreate("method.count.redirected.head", INKSTAT_TYPE_FLOAT);
method_count_redirected_icp_query = INKStatCreate("method.count.redirected.icp_query", INKSTAT_TYPE_FLOAT);
method_count_redirected_options = INKStatCreate("method.count.redirected.options", INKSTAT_TYPE_INT64);
method_count_redirected_post = INKStatCreate("method.count.redirected.post", INKSTAT_TYPE_INT64);
method_count_redirected_purge = INKStatCreate("method.count.redirected.purge", INKSTAT_TYPE_INT64);
method_count_redirected_put = INKStatCreate("method.count.redirected.put", INKSTAT_TYPE_INT64);
method_count_redirected_trace = INKStatCreate("method.count.redirected.trace", INKSTAT_TYPE_INT64);
method_count_redirected_unknown = INKStatCreate("method.count.redirected.unknown", INKSTAT_TYPE_INT64);
/* coupled: */
request_outcomes = INKStatCoupledGlobalCategoryCreate("request_outcomes");
requests_all = INKStatCoupledGlobalAdd(request_outcomes, "requests.all", INKSTAT_TYPE_FLOAT);
requests_redirects = INKStatCoupledGlobalAdd(request_outcomes, "requests.redirects", INKSTAT_TYPE_INT64);
requests_unchanged = INKStatCoupledGlobalAdd(request_outcomes, "requests.unchanged", INKSTAT_TYPE_INT64);
}
/*
* This function is only called for redirected requests. It illustrates
* several different ways of updating INT64 stats. Some may consider
* the particular use of INKDecrementStat() shown below somewhat contrived.
*/
void
update_redirected_method_stats(INKMBuffer bufp, INKMLoc hdr_loc)
{
const char *txn_method;
int length;
INK64 tempint;
txn_method = INKHttpHdrMethodGet(bufp, hdr_loc, &length);
if (NULL != txn_method) {
if (0 == strncmp(txn_method, INK_HTTP_METHOD_CONNECT, length))
INKStatIncrement(method_count_redirected_connect);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_DELETE, length))
INKStatIncrement(method_count_redirected_delete);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_GET, length))
INKStatIncrement(method_count_redirected_get);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_HEAD, length))
INKStatFloatAddTo(method_count_redirected_head, 1);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_ICP_QUERY, length))
INKStatFloatAddTo(method_count_redirected_icp_query, 1);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_OPTIONS, length)) {
tempint = INKStatIntRead(method_count_redirected_options);
tempint++;
INKStatIntSet(method_count_redirected_options, tempint);
} else if (0 == strncmp(txn_method, INK_HTTP_METHOD_POST, length)) {
INKStatDecrement(method_count_redirected_post);
INKStatIncrement(method_count_redirected_post);
INKStatIncrement(method_count_redirected_post);
}
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_PURGE, length))
INKStatIncrement(method_count_redirected_purge);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_PUT, length))
INKStatIncrement(method_count_redirected_put);
else if (0 == strncmp(txn_method, INK_HTTP_METHOD_TRACE, length))
INKStatIncrement(method_count_redirected_trace);
else
INKStatIncrement(method_count_redirected_unknown);
}
INKHandleStringRelease(bufp, hdr_loc, txn_method);
}
int
check_ts_version()
{
const char *ts_version = INKTrafficServerVersionGet();
int result = 0;
if (ts_version) {
int major_ts_version = 0;
int minor_ts_version = 0;
int patch_ts_version = 0;
if (sscanf(ts_version, "%d.%d.%d", &major_ts_version, &minor_ts_version, &patch_ts_version) != 3) {
return 0;
}
/* Need at least TS 2.0 */
if (major_ts_version >= 2) {
result = 1;
}
}
return result;
}
void
INKPluginInit(int argc, const char *argv[])
{
const char prefix[] = "http://";
int uri_len;
INKPluginRegistrationInfo info;
info.plugin_name = "redirect-1";
info.vendor_name = "MyCompany";
info.support_email = "ts-api-support@MyCompany.com";
if (!INKPluginRegister(INK_SDK_VERSION_2_0, &info)) {
INKError("Plugin registration failed.\n");
}
if (!check_ts_version()) {
INKError("Plugin requires Traffic Server 2.0 or later\n");
return;
}
if (argc == 3) {
block_ip = INKstrdup(argv[1]);
/*
* The Location header must contain an absolute URI:
*/
url_redirect = INKstrdup(argv[2]);
uri_len = strlen(prefix) + strlen(url_redirect) + 1;
uri_redirect = INKmalloc(uri_len);
strcpy(uri_redirect, prefix);
strcat(uri_redirect, url_redirect);
} else {
INKError("Incorrect syntax in plugin.conf: correct usage is" "redirect-1.so ip_deny url_redirect");
return;
}
ip_deny = inet_addr(block_ip);
INKDebug("redirect_init", "initializing stats...");
init_stats();
INKHttpHookAdd(INK_HTTP_READ_REQUEST_HDR_HOOK, INKContCreate(redirect_plugin, NULL));
INKDebug("redirect_init", "block_ip is %s, url_redirect is %s, and uri_redirect is %s",
block_ip, url_redirect, uri_redirect);
INKDebug("redirect_init", "ip_deny is %ld\n", ip_deny);
/*
* Demonstrate another tracing function. This can be used to
* enable debug calculations and other work that should only
* be done in debug mode.
*/
if (INKIsDebugTagSet("redirect_demo"))
INKDebug("redirect_init", "The redirect_demo tag is set");
else
INKDebug("redirect_init", "The redirect_demo tag is not set");
}