blob: f2c69b264861e845787665b6b92ed10238ad940f [file] [log] [blame]
/*
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 <ts/remap.h>
#include <cstdio>
#include <unistd.h>
#include <mysql/mysql.h>
#include "lib/iniparser.h"
#include "default.h"
MYSQL mysql;
typedef struct {
char *query;
} my_data;
bool
do_mysql_remap(TSCont contp, TSHttpTxn txnp)
{
TSMBuffer reqp;
TSMLoc hdr_loc, url_loc, field_loc;
bool ret_val = false;
const char *request_host;
int request_host_length = 0;
const char *request_scheme;
int request_scheme_length = 0;
int request_port = 80;
char *query;
MYSQL_ROW row;
MYSQL_RES *res;
my_data *data = static_cast<my_data *>(TSContDataGet(contp));
query = data->query;
if (TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc) != TS_SUCCESS) {
TSDebug(PLUGIN_NAME, "could not get request data");
return false;
}
TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc);
if (!url_loc) {
TSDebug(PLUGIN_NAME, "couldn't retrieve request url");
goto release_hdr;
}
field_loc = TSMimeHdrFieldFind(reqp, hdr_loc, TS_MIME_FIELD_HOST, TS_MIME_LEN_HOST);
if (!field_loc) {
TSDebug(PLUGIN_NAME, "couldn't retrieve request HOST header");
goto release_url;
}
request_host = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, field_loc, -1, &request_host_length);
if (!request_host_length) {
TSDebug(PLUGIN_NAME, "couldn't find request HOST header");
goto release_field;
}
request_scheme = TSUrlSchemeGet(reqp, url_loc, &request_scheme_length);
request_port = TSUrlPortGet(reqp, url_loc);
TSDebug(PLUGIN_NAME, " +++++MYSQL REMAP+++++ ");
TSDebug(PLUGIN_NAME, "\nINCOMING REQUEST ->\n ::: from_scheme_desc: %.*s\n ::: from_hostname: %.*s\n ::: from_port: %d",
request_scheme_length, request_scheme, request_host_length, request_host, request_port);
snprintf(query, QSIZE, " \
SELECT \
t_scheme.scheme_desc, \
t_host.hostname, \
to_port \
FROM map \
INNER JOIN scheme as t_scheme ON (map.to_scheme_id = t_scheme.id) \
INNER JOIN scheme as f_scheme ON (map.from_scheme_id = f_scheme.id) \
INNER JOIN hostname as t_host ON (map.to_hostname_id = t_host.id) \
INNER JOIN hostname as f_host ON (map.from_hostname_id = f_host.id) \
WHERE \
is_enabled=1 \
AND f_host.hostname = '%.*s' \
AND f_scheme.id = %d \
AND from_port = %d \
LIMIT 1",
request_host_length, request_host, (strcmp(request_scheme, "https") == 0) ? 2 : 1, request_port);
mysql_real_query(&mysql, query, (unsigned int)strlen(query));
res = mysql_use_result(&mysql);
if (!res)
goto not_found; // TODO: define a fallback
do {
row = mysql_fetch_row(res);
if (!row)
goto not_found;
TSDebug(PLUGIN_NAME, "\nOUTGOING REQUEST ->\n ::: to_scheme_desc: %s\n ::: to_hostname: %s\n ::: to_port: %s", row[0], row[1],
row[2]);
TSMimeHdrFieldValueStringSet(reqp, hdr_loc, field_loc, 0, row[1], -1);
TSUrlHostSet(reqp, url_loc, row[1], -1);
TSUrlSchemeSet(reqp, url_loc, row[0], -1);
TSUrlPortSet(reqp, url_loc, atoi(row[2]));
} while (false);
ret_val = true;
not_found:
if (!ret_val) {
// lets build up a nice 404 message for someone
TSHttpHdrStatusSet(reqp, hdr_loc, TS_HTTP_STATUS_NOT_FOUND);
TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_NOT_FOUND);
}
if (res) {
mysql_free_result(res);
}
release_field:
if (field_loc) {
TSHandleMLocRelease(reqp, hdr_loc, field_loc);
}
release_url:
if (url_loc) {
TSHandleMLocRelease(reqp, hdr_loc, url_loc);
}
release_hdr:
if (hdr_loc) {
TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
}
return ret_val;
}
static int
mysql_remap(TSCont contp, TSEvent event, void *edata)
{
TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
TSEvent reenable = TS_EVENT_HTTP_CONTINUE;
switch (event) {
case TS_EVENT_HTTP_READ_REQUEST_HDR:
TSDebug(PLUGIN_NAME, "Reading Request");
TSSkipRemappingSet(txnp, 1);
if (!do_mysql_remap(contp, txnp)) {
reenable = TS_EVENT_HTTP_ERROR;
}
break;
default:
break;
}
TSHttpTxnReenable(txnp, reenable);
return 1;
}
void
TSPluginInit(int argc, const char *argv[])
{
dictionary *ini;
const char *host;
int port;
const char *username;
const char *password;
const char *db;
my_data *data = static_cast<my_data *>(malloc(1 * sizeof(my_data)));
TSPluginRegistrationInfo info;
bool reconnect = true;
info.plugin_name = const_cast<char *>(PLUGIN_NAME);
info.vendor_name = const_cast<char *>("Apache Software Foundation");
info.support_email = const_cast<char *>("dev@trafficserver.apache.org");
if (TSPluginRegister(&info) != TS_SUCCESS) {
TSError("[mysql_remap] Plugin registration failed");
}
if (argc != 2) {
TSError("[mysql_remap] Usage: %s /path/to/sample.ini", argv[0]);
return;
}
ini = iniparser_load(argv[1]);
if (!ini) {
TSError("[mysql_remap] Error with ini file (1)");
TSDebug(PLUGIN_NAME, "Error parsing ini file(1)");
return;
}
host = iniparser_getstring(ini, "mysql_remap:mysql_host", (char *)"localhost");
port = iniparser_getint(ini, "mysql_remap:mysql_port", 3306);
username = iniparser_getstring(ini, "mysql_remap:mysql_username", nullptr);
password = iniparser_getstring(ini, "mysql_remap:mysql_password", nullptr);
db = iniparser_getstring(ini, "mysql_remap:mysql_database", (char *)"mysql_remap");
if (mysql_library_init(0, NULL, NULL)) {
TSError("[mysql_remap] Error initializing mysql client library");
TSDebug(PLUGIN_NAME, "Error initializing mysql client library");
return;
}
if (!mysql_init(&mysql)) {
TSError("[mysql_remap] Could not initialize MySQL");
TSDebug(PLUGIN_NAME, "Could not initialize MySQL");
return;
}
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);
if (!mysql_real_connect(&mysql, host, username, password, db, port, NULL, 0)) {
TSError("[mysql_remap] Could not connect to mysql");
TSDebug(PLUGIN_NAME, "Could not connect to mysql: %s", mysql_error(&mysql));
return;
}
data->query = static_cast<char *>(TSmalloc(QSIZE * sizeof(char))); // TODO: malloc smarter sizes
TSDebug(PLUGIN_NAME, "h: %s; u: %s; p: %s; p:%d; d:%s", host, username, password, port, db);
TSCont cont = TSContCreate(mysql_remap, TSMutexCreate());
TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, cont);
TSContDataSet(cont, (void *)data);
TSDebug(PLUGIN_NAME, "plugin is successfully initialized [plugin mode]");
iniparser_freedict(ini);
return;
}