1 /*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18
19 #include <ts/ts.h>
20 #include <ts/remap.h>
21 #include <cstdio>
22 #include <unistd.h>
23
24 #include <mysql/mysql.h>
25
26 #include "lib/iniparser.h"
27 #include "default.h"
28
29 MYSQL mysql;
30
31 using my_data = struct {
32 char *query;
33 };
34
35 bool
do_mysql_remap(TSCont contp,TSHttpTxn txnp)36 do_mysql_remap(TSCont contp, TSHttpTxn txnp)
37 {
38 TSMBuffer reqp;
39 TSMLoc hdr_loc, url_loc, field_loc;
40 bool ret_val = false;
41
42 const char *request_host;
43 int request_host_length = 0;
44 const char *request_scheme;
45 int request_scheme_length = 0;
46 int request_port = 80;
47 char *query;
48
49 MYSQL_ROW row;
50 MYSQL_RES *res;
51
52 my_data *data = static_cast<my_data *>(TSContDataGet(contp));
53 query = data->query;
54
55 if (TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc) != TS_SUCCESS) {
56 TSDebug(PLUGIN_NAME, "could not get request data");
57 return false;
58 }
59
60 TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc);
61
62 if (!url_loc) {
63 TSDebug(PLUGIN_NAME, "couldn't retrieve request url");
64 goto release_hdr;
65 }
66
67 field_loc = TSMimeHdrFieldFind(reqp, hdr_loc, TS_MIME_FIELD_HOST, TS_MIME_LEN_HOST);
68
69 if (!field_loc) {
70 TSDebug(PLUGIN_NAME, "couldn't retrieve request HOST header");
71 goto release_url;
72 }
73
74 request_host = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, field_loc, -1, &request_host_length);
75 if (!request_host_length) {
76 TSDebug(PLUGIN_NAME, "couldn't find request HOST header");
77 goto release_field;
78 }
79
80 request_scheme = TSUrlSchemeGet(reqp, url_loc, &request_scheme_length);
81 request_port = TSUrlPortGet(reqp, url_loc);
82
83 TSDebug(PLUGIN_NAME, " +++++MYSQL REMAP+++++ ");
84
85 TSDebug(PLUGIN_NAME, "\nINCOMING REQUEST ->\n ::: from_scheme_desc: %.*s\n ::: from_hostname: %.*s\n ::: from_port: %d",
86 request_scheme_length, request_scheme, request_host_length, request_host, request_port);
87
88 snprintf(query, QSIZE, " \
89 SELECT \
90 t_scheme.scheme_desc, \
91 t_host.hostname, \
92 to_port \
93 FROM map \
94 INNER JOIN scheme as t_scheme ON (map.to_scheme_id = t_scheme.id) \
95 INNER JOIN scheme as f_scheme ON (map.from_scheme_id = f_scheme.id) \
96 INNER JOIN hostname as t_host ON (map.to_hostname_id = t_host.id) \
97 INNER JOIN hostname as f_host ON (map.from_hostname_id = f_host.id) \
98 WHERE \
99 is_enabled=1 \
100 AND f_host.hostname = '%.*s' \
101 AND f_scheme.id = %d \
102 AND from_port = %d \
103 LIMIT 1",
104 request_host_length, request_host, (strcmp(request_scheme, "https") == 0) ? 2 : 1, request_port);
105
106 mysql_real_query(&mysql, query, (unsigned int)strlen(query));
107 res = mysql_use_result(&mysql);
108
109 if (!res)
110 goto not_found; // TODO: define a fallback
111
112 do {
113 row = mysql_fetch_row(res);
114 if (!row)
115 goto not_found;
116 TSDebug(PLUGIN_NAME, "\nOUTGOING REQUEST ->\n ::: to_scheme_desc: %s\n ::: to_hostname: %s\n ::: to_port: %s", row[0], row[1],
117 row[2]);
118 TSMimeHdrFieldValueStringSet(reqp, hdr_loc, field_loc, 0, row[1], -1);
119 TSUrlHostSet(reqp, url_loc, row[1], -1);
120 TSUrlSchemeSet(reqp, url_loc, row[0], -1);
121 TSUrlPortSet(reqp, url_loc, atoi(row[2]));
122 } while (false);
123
124 ret_val = true;
125
126 not_found:
127 if (!ret_val) {
128 // lets build up a nice 404 message for someone
129 TSHttpHdrStatusSet(reqp, hdr_loc, TS_HTTP_STATUS_NOT_FOUND);
130 TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_NOT_FOUND);
131 }
132 if (res) {
133 mysql_free_result(res);
134 }
135 release_field:
136 if (field_loc) {
137 TSHandleMLocRelease(reqp, hdr_loc, field_loc);
138 }
139 release_url:
140 if (url_loc) {
141 TSHandleMLocRelease(reqp, hdr_loc, url_loc);
142 }
143 release_hdr:
144 if (hdr_loc) {
145 TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
146 }
147
148 return ret_val;
149 }
150
151 static int
mysql_remap(TSCont contp,TSEvent event,void * edata)152 mysql_remap(TSCont contp, TSEvent event, void *edata)
153 {
154 TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
155 TSEvent reenable = TS_EVENT_HTTP_CONTINUE;
156
157 switch (event) {
158 case TS_EVENT_HTTP_READ_REQUEST_HDR:
159 TSDebug(PLUGIN_NAME, "Reading Request");
160 TSSkipRemappingSet(txnp, 1);
161 if (!do_mysql_remap(contp, txnp)) {
162 reenable = TS_EVENT_HTTP_ERROR;
163 }
164 break;
165 default:
166 break;
167 }
168
169 TSHttpTxnReenable(txnp, reenable);
170 return 1;
171 }
172
173 void
TSPluginInit(int argc,const char * argv[])174 TSPluginInit(int argc, const char *argv[])
175 {
176 dictionary *ini;
177 const char *host;
178 int port;
179 const char *username;
180 const char *password;
181 const char *db;
182
183 my_data *data = static_cast<my_data *>(malloc(1 * sizeof(my_data)));
184
185 TSPluginRegistrationInfo info;
186 bool reconnect = true;
187
188 info.plugin_name = const_cast<char *>(PLUGIN_NAME);
189 info.vendor_name = const_cast<char *>("Apache Software Foundation");
190 info.support_email = const_cast<char *>("dev@trafficserver.apache.org");
191
192 if (TSPluginRegister(&info) != TS_SUCCESS) {
193 TSError("[mysql_remap] Plugin registration failed");
194 }
195
196 if (argc != 2) {
197 TSError("[mysql_remap] Usage: %s /path/to/sample.ini", argv[0]);
198 return;
199 }
200
201 ini = iniparser_load(argv[1]);
202 if (!ini) {
203 TSError("[mysql_remap] Error with ini file (1)");
204 TSDebug(PLUGIN_NAME, "Error parsing ini file(1)");
205 return;
206 }
207
208 host = iniparser_getstring(ini, "mysql_remap:mysql_host", (char *)"localhost");
209 port = iniparser_getint(ini, "mysql_remap:mysql_port", 3306);
210 username = iniparser_getstring(ini, "mysql_remap:mysql_username", nullptr);
211 password = iniparser_getstring(ini, "mysql_remap:mysql_password", nullptr);
212 db = iniparser_getstring(ini, "mysql_remap:mysql_database", (char *)"mysql_remap");
213
214 if (mysql_library_init(0, NULL, NULL)) {
215 TSError("[mysql_remap] Error initializing mysql client library");
216 TSDebug(PLUGIN_NAME, "Error initializing mysql client library");
217 return;
218 }
219
220 if (!mysql_init(&mysql)) {
221 TSError("[mysql_remap] Could not initialize MySQL");
222 TSDebug(PLUGIN_NAME, "Could not initialize MySQL");
223 return;
224 }
225
226 mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);
227
228 if (!mysql_real_connect(&mysql, host, username, password, db, port, NULL, 0)) {
229 TSError("[mysql_remap] Could not connect to mysql");
230 TSDebug(PLUGIN_NAME, "Could not connect to mysql: %s", mysql_error(&mysql));
231 return;
232 }
233
234 data->query = static_cast<char *>(TSmalloc(QSIZE * sizeof(char))); // TODO: malloc smarter sizes
235
236 TSDebug(PLUGIN_NAME, "h: %s; u: %s; p: %s; p:%d; d:%s", host, username, password, port, db);
237 TSCont cont = TSContCreate(mysql_remap, TSMutexCreate());
238
239 TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, cont);
240
241 TSContDataSet(cont, (void *)data);
242
243 TSDebug(PLUGIN_NAME, "plugin is successfully initialized [plugin mode]");
244 iniparser_freedict(ini);
245 return;
246 }
247