client-tags.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*********************************************************************
  2. *
  3. * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
  4. *
  5. * Purpose : Functions related to client-specific tags.
  6. *
  7. * Copyright : Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
  8. *
  9. * This program is free software; you can redistribute it
  10. * and/or modify it under the terms of the GNU General
  11. * Public License as published by the Free Software
  12. * Foundation; either version 2 of the License, or (at
  13. * your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will
  16. * be useful, but WITHOUT ANY WARRANTY; without even the
  17. * implied warranty of MERCHANTABILITY or FITNESS FOR A
  18. * PARTICULAR PURPOSE. See the GNU General Public
  19. * License for more details.
  20. *
  21. * The GNU General Public License should be included with
  22. * this file. If not, you can view it at
  23. * http://www.gnu.org/copyleft/gpl.html
  24. * or write to the Free Software Foundation, Inc., 59
  25. * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  26. *
  27. **********************************************************************/
  28. #include "config.h"
  29. #ifdef FEATURE_CLIENT_TAGS
  30. #include <stdio.h>
  31. #include <sys/types.h>
  32. #include <stdlib.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <assert.h>
  36. #include "project.h"
  37. #include "list.h"
  38. #include "jcc.h"
  39. #include "miscutil.h"
  40. #include "errlog.h"
  41. #include "parsers.h"
  42. struct client_specific_tag
  43. {
  44. char *name;
  45. time_t end_of_life;
  46. struct client_specific_tag *next;
  47. struct client_specific_tag *prev;
  48. };
  49. /**
  50. * This struct represents tags that have been requested by clients
  51. */
  52. struct requested_tags
  53. {
  54. char *client; /**< The IP address of the client that requested the tag */
  55. /**< List of tags the client requested .... */
  56. struct client_specific_tag *tags;
  57. struct requested_tags *next;
  58. struct requested_tags *prev;
  59. };
  60. struct requested_tags *requested_tags;
  61. static void remove_tag_for_client(const char *client_address, const char *tag);
  62. /*********************************************************************
  63. *
  64. * Function : validate_tag_list
  65. *
  66. * Description : Validates the given tag list
  67. *
  68. * Parameters :
  69. * 1 : enabled_tags = The tags to validate
  70. *
  71. * Returns : void
  72. *
  73. *********************************************************************/
  74. static void validate_tag_list(struct client_specific_tag *enabled_tags)
  75. {
  76. while (enabled_tags != NULL)
  77. {
  78. if (enabled_tags->name == NULL)
  79. {
  80. assert(enabled_tags->name != NULL);
  81. log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
  82. }
  83. if (enabled_tags->next != NULL)
  84. {
  85. if (enabled_tags->next->prev != enabled_tags)
  86. {
  87. assert(enabled_tags->next->prev == enabled_tags);
  88. log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
  89. }
  90. }
  91. enabled_tags = enabled_tags->next;
  92. }
  93. }
  94. /*********************************************************************
  95. *
  96. * Function : validate_requested_tags
  97. *
  98. * Description : Validates the requested_tags list
  99. *
  100. * Parameters : N/A
  101. *
  102. * Returns : void
  103. *
  104. *********************************************************************/
  105. static jb_err validate_requested_tags()
  106. {
  107. struct requested_tags *requested_tag;
  108. for (requested_tag = requested_tags; requested_tag != NULL;
  109. requested_tag = requested_tag->next)
  110. {
  111. if (requested_tag->client == NULL)
  112. {
  113. assert(requested_tag->client != NULL);
  114. log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
  115. }
  116. validate_tag_list(requested_tag->tags);
  117. if (requested_tag->next != NULL)
  118. {
  119. if (requested_tag->next->prev != requested_tag)
  120. {
  121. assert(requested_tag->next->prev == requested_tag);
  122. log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
  123. }
  124. }
  125. }
  126. return TRUE;
  127. }
  128. /*********************************************************************
  129. *
  130. * Function : get_client_specific_tag
  131. *
  132. * Description : Returns the data for a client-specific-tag specified
  133. * by name.
  134. *
  135. * Parameters :
  136. * 1 : tag_list = The tag list to check
  137. * 2 : name = The tag name to look up
  138. *
  139. * Returns : Pointer to tag structure or NULL on error.
  140. *
  141. *********************************************************************/
  142. static struct client_tag_spec *get_client_specific_tag(
  143. struct client_tag_spec *tag_list, const char *name)
  144. {
  145. struct client_tag_spec *tag;
  146. for (tag = tag_list; tag != NULL; tag = tag->next)
  147. {
  148. if (tag->name != NULL && !strcmp(tag->name, name))
  149. {
  150. return tag;
  151. }
  152. }
  153. log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
  154. return NULL;
  155. }
  156. /*********************************************************************
  157. *
  158. * Function : get_tags_for_client
  159. *
  160. * Description : Returns the list of tags the client opted-in.
  161. *
  162. * Parameters :
  163. * 1 : client_address = Address of the client
  164. *
  165. * Returns : Pointer to tag structure or NULL on error.
  166. *
  167. *********************************************************************/
  168. static struct client_specific_tag *get_tags_for_client(const char *client_address)
  169. {
  170. struct requested_tags *requested_tag;
  171. for (requested_tag = requested_tags; requested_tag != NULL;
  172. requested_tag = requested_tag->next)
  173. {
  174. if (!strcmp(requested_tag->client, client_address))
  175. {
  176. return requested_tag->tags;
  177. }
  178. }
  179. return NULL;
  180. }
  181. /*********************************************************************
  182. *
  183. * Function : get_tag_list_for_client
  184. *
  185. * Description : Provides a list of tag names the client opted-in.
  186. * Other tag attributes are not part of the list.
  187. *
  188. * Parameters :
  189. * 1 : tag_list = The list to fill in.
  190. * 2 : client_address = Address of the client
  191. *
  192. * Returns : Pointer to tag list.
  193. *
  194. *********************************************************************/
  195. void get_tag_list_for_client(struct list *tag_list,
  196. const char *client_address)
  197. {
  198. struct client_specific_tag *enabled_tags;
  199. const time_t now = time(NULL);
  200. privoxy_mutex_lock(&client_tags_mutex);
  201. enabled_tags = get_tags_for_client(client_address);
  202. while (enabled_tags != NULL)
  203. {
  204. if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
  205. {
  206. struct client_specific_tag *next_tag = enabled_tags->next;
  207. log_error(LOG_LEVEL_INFO,
  208. "Tag '%s' for client %s expired %ld seconds ago. Deleting it.",
  209. enabled_tags->name, client_address,
  210. (now - enabled_tags->end_of_life));
  211. remove_tag_for_client(client_address, enabled_tags->name);
  212. enabled_tags = next_tag;
  213. continue;
  214. }
  215. else
  216. {
  217. enlist(tag_list, enabled_tags->name);
  218. }
  219. enabled_tags = enabled_tags->next;
  220. }
  221. privoxy_mutex_unlock(&client_tags_mutex);
  222. }
  223. /*********************************************************************
  224. *
  225. * Function : get_next_tag_timeout_for_client
  226. *
  227. * Description : Figures out when the next temporarily enabled tag
  228. * for the client will have timed out.
  229. *
  230. * Parameters :
  231. * 1 : client_address = Address of the client
  232. *
  233. * Returns : Lowest timeout in seconds
  234. *
  235. *********************************************************************/
  236. time_t get_next_tag_timeout_for_client(const char *client_address)
  237. {
  238. struct client_specific_tag *enabled_tags;
  239. time_t next_timeout = 0;
  240. const time_t now = time(NULL);
  241. privoxy_mutex_lock(&client_tags_mutex);
  242. enabled_tags = get_tags_for_client(client_address);
  243. while (enabled_tags != NULL)
  244. {
  245. log_error(LOG_LEVEL_CGI, "Evaluating tag '%s' for client %s. End of life %ld",
  246. enabled_tags->name, client_address, enabled_tags->end_of_life);
  247. if (enabled_tags->end_of_life)
  248. {
  249. time_t time_left = enabled_tags->end_of_life - now;
  250. /* Add a second to make sure the tag will have expired */
  251. time_left++;
  252. log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
  253. if (next_timeout == 0 || next_timeout > time_left)
  254. {
  255. next_timeout = time_left;
  256. }
  257. }
  258. enabled_tags = enabled_tags->next;
  259. }
  260. privoxy_mutex_unlock(&client_tags_mutex);
  261. log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
  262. return next_timeout;
  263. }
  264. /*********************************************************************
  265. *
  266. * Function : create_client_specific_tag
  267. *
  268. * Description : Allocates memory for a client specific tag
  269. * and populates it.
  270. *
  271. * Parameters :
  272. * 1 : name = The name of the tag to create.
  273. * 2 : time_to_live = 0, or the number of seconds
  274. * the tag remains activated.
  275. *
  276. * Returns : Pointer to populated tag
  277. *
  278. *********************************************************************/
  279. static struct client_specific_tag *create_client_specific_tag(const char *name,
  280. const time_t time_to_live)
  281. {
  282. struct client_specific_tag *tag;
  283. tag = zalloc_or_die(sizeof(struct client_specific_tag));
  284. tag->name = strdup_or_die(name);
  285. tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
  286. return tag;
  287. }
  288. /*********************************************************************
  289. *
  290. * Function : add_tag_for_client
  291. *
  292. * Description : Adds the tag for the client.
  293. *
  294. * Parameters :
  295. * 1 : client_address = Address of the client
  296. * 2 : tag = The tag to add.
  297. * 3 : time_to_live = 0, or the number of seconds
  298. * the tag remains activated.
  299. *
  300. * Returns : void
  301. *
  302. *********************************************************************/
  303. static void add_tag_for_client(const char *client_address,
  304. const char *tag, const time_t time_to_live)
  305. {
  306. struct requested_tags *clients_with_tags;
  307. struct client_specific_tag *enabled_tags;
  308. validate_requested_tags();
  309. if (requested_tags == NULL)
  310. {
  311. /* XXX: Code duplication. */
  312. requested_tags = zalloc_or_die(sizeof(struct requested_tags));
  313. requested_tags->client = strdup_or_die(client_address);
  314. requested_tags->tags = create_client_specific_tag(tag, time_to_live);
  315. validate_requested_tags();
  316. return;
  317. }
  318. else
  319. {
  320. clients_with_tags = requested_tags;
  321. while (clients_with_tags->next != NULL)
  322. {
  323. if (!strcmp(clients_with_tags->client, client_address))
  324. {
  325. break;
  326. }
  327. clients_with_tags = clients_with_tags->next;
  328. }
  329. if (strcmp(clients_with_tags->client, client_address))
  330. {
  331. /* Client does not have tags yet, add new structure */
  332. clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
  333. clients_with_tags->next->prev = clients_with_tags;
  334. clients_with_tags = clients_with_tags->next;
  335. clients_with_tags->client = strdup_or_die(client_address);
  336. clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
  337. validate_requested_tags();
  338. return;
  339. }
  340. }
  341. enabled_tags = clients_with_tags->tags;
  342. while (enabled_tags != NULL)
  343. {
  344. if (enabled_tags->next == NULL)
  345. {
  346. enabled_tags->next = create_client_specific_tag(tag, time_to_live);
  347. enabled_tags->next->prev = enabled_tags;
  348. break;
  349. }
  350. enabled_tags = enabled_tags->next;
  351. }
  352. validate_requested_tags();
  353. }
  354. /*********************************************************************
  355. *
  356. * Function : remove_tag_for_client
  357. *
  358. * Description : Removes the tag for the client.
  359. *
  360. * Parameters :
  361. * 1 : client_address = Address of the client
  362. * 2 : tag = The tag to remove.
  363. *
  364. * Returns : void
  365. *
  366. *********************************************************************/
  367. static void remove_tag_for_client(const char *client_address, const char *tag)
  368. {
  369. struct requested_tags *clients_with_tags;
  370. struct client_specific_tag *enabled_tags;
  371. validate_requested_tags();
  372. clients_with_tags = requested_tags;
  373. while (clients_with_tags != NULL && clients_with_tags->client != NULL)
  374. {
  375. if (!strcmp(clients_with_tags->client, client_address))
  376. {
  377. break;
  378. }
  379. clients_with_tags = clients_with_tags->next;
  380. }
  381. assert(clients_with_tags != NULL);
  382. if (clients_with_tags == NULL)
  383. {
  384. log_error(LOG_LEVEL_ERROR,
  385. "Tried to remove tag %s for tag-less client %s",
  386. tag, client_address);
  387. }
  388. enabled_tags = clients_with_tags->tags;
  389. while (enabled_tags != NULL)
  390. {
  391. if (!strcmp(enabled_tags->name, tag))
  392. {
  393. if (enabled_tags->next != NULL)
  394. {
  395. enabled_tags->next->prev = enabled_tags->prev;
  396. if (enabled_tags == clients_with_tags->tags)
  397. {
  398. /* Tag is first in line */
  399. clients_with_tags->tags = enabled_tags->next;
  400. }
  401. }
  402. if (enabled_tags->prev != NULL)
  403. {
  404. /* Tag has preceding tag */
  405. enabled_tags->prev->next = enabled_tags->next;
  406. }
  407. if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
  408. {
  409. /* Tag is the only one */
  410. if (clients_with_tags->next != NULL)
  411. {
  412. /* Client has following client */
  413. clients_with_tags->next->prev = clients_with_tags->prev;
  414. }
  415. if (clients_with_tags->prev != NULL)
  416. {
  417. /* Client has preceding client */
  418. clients_with_tags->prev->next = clients_with_tags->next;
  419. }
  420. if (clients_with_tags == requested_tags)
  421. {
  422. /*
  423. * We're in the process of removing the last tag,
  424. * mark the global list as empty.
  425. */
  426. requested_tags = NULL;
  427. }
  428. freez(clients_with_tags->client);
  429. freez(clients_with_tags);
  430. }
  431. freez(enabled_tags->name);
  432. freez(enabled_tags);
  433. break;
  434. }
  435. enabled_tags = enabled_tags->next;
  436. }
  437. validate_requested_tags();
  438. }
  439. /*********************************************************************
  440. *
  441. * Function : client_has_requested_tag
  442. *
  443. * Description : Checks whether or not the given client requested
  444. * the tag.
  445. *
  446. * Parameters :
  447. * 1 : client_address = Address of the client
  448. * 2 : tag = Tag to check.
  449. *
  450. * Returns : TRUE or FALSE.
  451. *
  452. *********************************************************************/
  453. int client_has_requested_tag(const char *client_address, const char *tag)
  454. {
  455. struct client_specific_tag *enabled_tags;
  456. enabled_tags = get_tags_for_client(client_address);
  457. while (enabled_tags != NULL)
  458. {
  459. if (!strcmp(enabled_tags->name, tag))
  460. {
  461. return TRUE;
  462. }
  463. enabled_tags = enabled_tags->next;
  464. }
  465. return FALSE;
  466. }
  467. /*********************************************************************
  468. *
  469. * Function : enable_client_specific_tag
  470. *
  471. * Description : Enables a client-specific-tag for the client
  472. *
  473. * Parameters :
  474. * 1 : csp = Current client state (buffers, headers, etc...)
  475. * 2 : tag_name = The name of the tag to enable
  476. * 3 : time_to_live = If not 0, the number of seconds the
  477. * tag should stay enabled.
  478. *
  479. * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
  480. *
  481. *********************************************************************/
  482. jb_err enable_client_specific_tag(struct client_state *csp,
  483. const char *tag_name, const time_t time_to_live)
  484. {
  485. struct client_tag_spec *tag;
  486. privoxy_mutex_lock(&client_tags_mutex);
  487. tag = get_client_specific_tag(csp->config->client_tags, tag_name);
  488. if (tag == NULL)
  489. {
  490. privoxy_mutex_unlock(&client_tags_mutex);
  491. return JB_ERR_PARSE;
  492. }
  493. if (client_has_requested_tag(csp->client_address, tag_name))
  494. {
  495. log_error(LOG_LEVEL_ERROR,
  496. "Tag '%s' already enabled for client '%s'", tag->name, csp->client_address);
  497. }
  498. else
  499. {
  500. add_tag_for_client(csp->client_address, tag_name, time_to_live);
  501. log_error(LOG_LEVEL_INFO,
  502. "Tag '%s' enabled for client '%s'. TTL: %ld.",
  503. tag->name, csp->client_address, time_to_live);
  504. }
  505. privoxy_mutex_unlock(&client_tags_mutex);
  506. return JB_ERR_OK;
  507. }
  508. /*********************************************************************
  509. *
  510. * Function : disable_client_specific_tag
  511. *
  512. * Description : Disables a client-specific-tag for the client
  513. *
  514. * Parameters :
  515. * 1 : csp = Current client state (buffers, headers, etc...)
  516. * 2 : tag_name = The name of the tag to disable
  517. *
  518. * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
  519. *
  520. *********************************************************************/
  521. jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
  522. {
  523. struct client_tag_spec *tag;
  524. privoxy_mutex_lock(&client_tags_mutex);
  525. tag = get_client_specific_tag(csp->config->client_tags, tag_name);
  526. if (tag == NULL)
  527. {
  528. privoxy_mutex_unlock(&client_tags_mutex);
  529. return JB_ERR_PARSE;
  530. }
  531. if (client_has_requested_tag(csp->client_address, tag_name))
  532. {
  533. remove_tag_for_client(csp->client_address, tag_name);
  534. log_error(LOG_LEVEL_INFO,
  535. "Tag '%s' disabled for client '%s'", tag->name, csp->client_address);
  536. }
  537. else
  538. {
  539. log_error(LOG_LEVEL_ERROR,
  540. "Tag '%s' currently not set for client '%s'",
  541. tag->name, csp->client_address);
  542. }
  543. privoxy_mutex_unlock(&client_tags_mutex);
  544. return JB_ERR_OK;
  545. }
  546. /*********************************************************************
  547. *
  548. * Function : client_tag_match
  549. *
  550. * Description : Compare a client tag against a client tag pattern.
  551. *
  552. * Parameters :
  553. * 1 : pattern = a TAG pattern
  554. * 2 : tag = Client tag to match
  555. *
  556. * Returns : Nonzero if the tag matches the pattern, else 0.
  557. *
  558. *********************************************************************/
  559. int client_tag_match(const struct pattern_spec *pattern,
  560. const struct list *tags)
  561. {
  562. struct list_entry *tag;
  563. if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
  564. {
  565. /*
  566. * It's not a client pattern and thus shouldn't
  567. * be matched against client tags.
  568. */
  569. return 0;
  570. }
  571. assert(tags);
  572. for (tag = tags->first; tag != NULL; tag = tag->next)
  573. {
  574. if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
  575. {
  576. return 1;
  577. }
  578. }
  579. return 0;
  580. }
  581. /*********************************************************************
  582. *
  583. * Function : set_client_address
  584. *
  585. * Description : Sets the client address that will be used to enable,
  586. * disable, or apply client tags.
  587. *
  588. * Parameters :
  589. * 1 : csp = Current client state (buffers, headers, etc...)
  590. * 2 : headers = Client headers
  591. *
  592. * Returns : void.
  593. *
  594. *********************************************************************/
  595. void set_client_address(struct client_state *csp, const struct list *headers)
  596. {
  597. if (csp->config->trust_x_forwarded_for)
  598. {
  599. const char *client_address;
  600. client_address = get_header_value(headers, "X-Forwarded-For:");
  601. if (client_address != NULL)
  602. {
  603. csp->client_address = strdup_or_die(client_address);
  604. log_error(LOG_LEVEL_HEADER,
  605. "Got client address %s from X-Forwarded-For header",
  606. csp->client_address);
  607. }
  608. }
  609. if (csp->client_address == NULL)
  610. {
  611. csp->client_address = strdup_or_die(csp->ip_addr_str);
  612. }
  613. }
  614. #else
  615. #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
  616. #endif /* def FEATURE_CLIENT_TAGS */