Sample Source Code

blacklist_1.c

The sample blacklisting plugin included in the Traffic Server SDK is blacklist_1.c. This plugin checks every incoming HTTP client request against a list of blacklisted web sites. If the client requests a blacklisted site, then the plugin returns an Access forbidden message to the client.

This plugin illustrates:

  • An HTTP transaction extension
  • How to examine HTTP request headers
  • How to use the logging interface
  • How to use the plugin configuration management interface
  1. /** @file
  2. An example plugin that denies client access to blacklisted sites (blacklist.txt).
  3. @section license License
  4. Licensed to the Apache Software Foundation (ASF) under one
  5. or more contributor license agreements. See the NOTICE file
  6. distributed with this work for additional information
  7. regarding copyright ownership. The ASF licenses this file
  8. to you under the Apache License, Version 2.0 (the
  9. "License"); you may not use this file except in compliance
  10. with the License. You may obtain a copy of the License at
  11. http://www.apache.org/licenses/LICENSE-2.0
  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. #include <stdio.h>
  19. #include <string.h>
  20. #include "ts/ts.h"
  21. #include "tscore/ink_defs.h"
  22. #define PLUGIN_NAME "blacklist_1"
  23. #define MAX_NSITES 500
  24. #define RETRY_TIME 10
  25. static char *sites[MAX_NSITES];
  26. static int nsites;
  27. static TSMutex sites_mutex;
  28. static TSTextLogObject log;
  29. static TSCont global_contp;
  30. static void handle_txn_start(TSCont contp, TSHttpTxn txnp);
  31. typedef struct contp_data {
  32. enum calling_func {
  33. HANDLE_DNS,
  34. HANDLE_RESPONSE,
  35. READ_BLACKLIST,
  36. } cf;
  37. TSHttpTxn txnp;
  38. } cdata;
  39. static void
  40. destroy_continuation(TSHttpTxn txnp, TSCont contp)
  41. {
  42. cdata *cd = NULL;
  43. cd = (cdata *)TSContDataGet(contp);
  44. if (cd != NULL) {
  45. TSfree(cd);
  46. }
  47. TSContDestroy(contp);
  48. TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  49. return;
  50. }
  51. static void
  52. handle_dns(TSHttpTxn txnp, TSCont contp)
  53. {
  54. TSMBuffer bufp;
  55. TSMLoc hdr_loc;
  56. TSMLoc url_loc;
  57. const char *host;
  58. int i;
  59. int host_length;
  60. if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
  61. TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME);
  62. goto done;
  63. }
  64. if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
  65. TSError("[%s] Couldn't retrieve request url", PLUGIN_NAME);
  66. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  67. goto done;
  68. }
  69. host = TSUrlHostGet(bufp, url_loc, &host_length);
  70. if (!host) {
  71. TSError("[%s] Couldn't retrieve request hostname", PLUGIN_NAME);
  72. TSHandleMLocRelease(bufp, hdr_loc, url_loc);
  73. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  74. goto done;
  75. }
  76. /* We need to lock the sites_mutex as that is the mutex that is
  77. protecting the global list of all blacklisted sites. */
  78. if (TSMutexLockTry(sites_mutex) != TS_SUCCESS) {
  79. TSDebug(PLUGIN_NAME, "Unable to get lock. Will retry after some time");
  80. TSHandleMLocRelease(bufp, hdr_loc, url_loc);
  81. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  82. TSContSchedule(contp, RETRY_TIME, TS_THREAD_POOL_DEFAULT);
  83. return;
  84. }
  85. for (i = 0; i < nsites; i++) {
  86. if (strncmp(host, sites[i], host_length) == 0) {
  87. if (log) {
  88. TSTextLogObjectWrite(log, "blacklisting site: %s", sites[i]);
  89. } else {
  90. TSDebug(PLUGIN_NAME, "blacklisting site: %s", sites[i]);
  91. }
  92. TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
  93. TSHandleMLocRelease(bufp, hdr_loc, url_loc);
  94. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  95. TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
  96. TSMutexUnlock(sites_mutex);
  97. return;
  98. }
  99. }
  100. TSMutexUnlock(sites_mutex);
  101. TSHandleMLocRelease(bufp, hdr_loc, url_loc);
  102. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  103. done:
  104. TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  105. }
  106. static void
  107. handle_response(TSHttpTxn txnp, TSCont contp ATS_UNUSED)
  108. {
  109. TSMBuffer bufp;
  110. TSMLoc hdr_loc;
  111. TSMLoc url_loc;
  112. char *url_str;
  113. char *buf;
  114. int url_length;
  115. if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
  116. TSError("[%s] Couldn't retrieve client response header", PLUGIN_NAME);
  117. goto done;
  118. }
  119. TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN);
  120. TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN),
  121. strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN)));
  122. if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
  123. TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME);
  124. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  125. goto done;
  126. }
  127. if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
  128. TSError("[%s] Couldn't retrieve request url", PLUGIN_NAME);
  129. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  130. goto done;
  131. }
  132. buf = (char *)TSmalloc(4096);
  133. url_str = TSUrlStringGet(bufp, url_loc, &url_length);
  134. sprintf(buf, "You are forbidden from accessing \"%s\"\n", url_str);
  135. TSfree(url_str);
  136. TSHandleMLocRelease(bufp, hdr_loc, url_loc);
  137. TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  138. TSHttpTxnErrorBodySet(txnp, buf, strlen(buf), NULL);
  139. done:
  140. TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  141. }
  142. static void
  143. read_blacklist(TSCont contp)
  144. {
  145. char blacklist_file[1024];
  146. TSFile file;
  147. sprintf(blacklist_file, "%s/blacklist.txt", TSPluginDirGet());
  148. file = TSfopen(blacklist_file, "r");
  149. nsites = 0;
  150. /* If the Mutex lock is not successful try again in RETRY_TIME */
  151. if (TSMutexLockTry(sites_mutex) != TS_SUCCESS) {
  152. if (file != NULL) {
  153. TSfclose(file);
  154. }
  155. TSContSchedule(contp, RETRY_TIME, TS_THREAD_POOL_DEFAULT);
  156. return;
  157. }
  158. if (file != NULL) {
  159. char buffer[1024];
  160. while (TSfgets(file, buffer, sizeof(buffer) - 1) != NULL && nsites < MAX_NSITES) {
  161. char *eol;
  162. if ((eol = strstr(buffer, "\r\n")) != NULL) {
  163. /* To handle newlines on Windows */
  164. *eol = '\0';
  165. } else if ((eol = strchr(buffer, '\n')) != NULL) {
  166. *eol = '\0';
  167. } else {
  168. /* Not a valid line, skip it */
  169. continue;
  170. }
  171. if (sites[nsites] != NULL) {
  172. TSfree(sites[nsites]);
  173. }
  174. sites[nsites] = TSstrdup(buffer);
  175. nsites++;
  176. }
  177. TSfclose(file);
  178. } else {
  179. TSError("[%s] Unable to open %s", PLUGIN_NAME, blacklist_file);
  180. TSError("[%s] All sites will be allowed", PLUGIN_NAME);
  181. }
  182. TSMutexUnlock(sites_mutex);
  183. }
  184. static int
  185. blacklist_plugin(TSCont contp, TSEvent event, void *edata)
  186. {
  187. TSHttpTxn txnp;
  188. cdata *cd;
  189. switch (event) {
  190. case TS_EVENT_HTTP_TXN_START:
  191. txnp = (TSHttpTxn)edata;
  192. handle_txn_start(contp, txnp);
  193. return 0;
  194. case TS_EVENT_HTTP_OS_DNS:
  195. if (contp != global_contp) {
  196. cd = (cdata *)TSContDataGet(contp);
  197. cd->cf = HANDLE_DNS;
  198. handle_dns(cd->txnp, contp);
  199. return 0;
  200. } else {
  201. break;
  202. }
  203. case TS_EVENT_HTTP_TXN_CLOSE:
  204. txnp = (TSHttpTxn)edata;
  205. if (contp != global_contp) {
  206. destroy_continuation(txnp, contp);
  207. }
  208. break;
  209. case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
  210. if (contp != global_contp) {
  211. cd = (cdata *)TSContDataGet(contp);
  212. cd->cf = HANDLE_RESPONSE;
  213. handle_response(cd->txnp, contp);
  214. return 0;
  215. } else {
  216. break;
  217. }
  218. case TS_EVENT_TIMEOUT:
  219. /* when mutex lock is not acquired and continuation is rescheduled,
  220. the plugin is called back with TS_EVENT_TIMEOUT with a NULL
  221. edata. We need to decide, in which function did the MutexLock
  222. failed and call that function again */
  223. if (contp != global_contp) {
  224. cd = (cdata *)TSContDataGet(contp);
  225. switch (cd->cf) {
  226. case HANDLE_DNS:
  227. handle_dns(cd->txnp, contp);
  228. return 0;
  229. case HANDLE_RESPONSE:
  230. handle_response(cd->txnp, contp);
  231. return 0;
  232. default:
  233. TSDebug(PLUGIN_NAME, "This event was unexpected: %d", event);
  234. break;
  235. }
  236. } else {
  237. read_blacklist(contp);
  238. return 0;
  239. }
  240. default:
  241. break;
  242. }
  243. return 0;
  244. }
  245. static void
  246. handle_txn_start(TSCont contp ATS_UNUSED, TSHttpTxn txnp)
  247. {
  248. TSCont txn_contp;
  249. cdata *cd;
  250. txn_contp = TSContCreate((TSEventFunc)blacklist_plugin, TSMutexCreate());
  251. /* create the data that'll be associated with the continuation */
  252. cd = (cdata *)TSmalloc(sizeof(cdata));
  253. TSContDataSet(txn_contp, cd);
  254. cd->txnp = txnp;
  255. TSHttpTxnHookAdd(txnp, TS_HTTP_OS_DNS_HOOK, txn_contp);
  256. TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
  257. TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  258. }
  259. void
  260. TSPluginInit(int argc ATS_UNUSED, const char *argv[] ATS_UNUSED)
  261. {
  262. int i;
  263. TSPluginRegistrationInfo info;
  264. TSReturnCode error;
  265. info.plugin_name = PLUGIN_NAME;
  266. info.vendor_name = "Apache Software Foundation";
  267. info.support_email = "dev@trafficserver.apache.org";
  268. if (TSPluginRegister(&info) != TS_SUCCESS) {
  269. TSError("[%s] Plugin registration failed", PLUGIN_NAME);
  270. }
  271. /* create an TSTextLogObject to log blacklisted requests to */
  272. error = TSTextLogObjectCreate("blacklist", TS_LOG_MODE_ADD_TIMESTAMP, &log);
  273. if (!log || error == TS_ERROR) {
  274. TSDebug(PLUGIN_NAME, "error while creating log");
  275. }
  276. sites_mutex = TSMutexCreate();
  277. nsites = 0;
  278. for (i = 0; i < MAX_NSITES; i++) {
  279. sites[i] = NULL;
  280. }
  281. global_contp = TSContCreate(blacklist_plugin, sites_mutex);
  282. read_blacklist(global_contp);
  283. /*TSHttpHookAdd (TS_HTTP_OS_DNS_HOOK, contp); */
  284. TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, global_contp);
  285. }