1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
5 * Purpose : Functions related to client-specific tags.
7 * Copyright : Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
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.
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.
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.
27 **********************************************************************/
31 #ifdef FEATURE_CLIENT_TAGS
34 #include <sys/types.h>
48 struct client_specific_tag
54 struct client_specific_tag *next;
55 struct client_specific_tag *prev;
59 * This struct represents tags that have been requested by clients
63 char *client; /**< The IP address of the client that requested the tag */
65 /**< List of tags the client requested .... */
66 struct client_specific_tag *tags;
68 struct requested_tags *next;
69 struct requested_tags *prev;
72 struct requested_tags *requested_tags;
73 static void remove_tag_for_client(const char *client_address, const char *tag);
75 /*********************************************************************
77 * Function : validate_tag_list
79 * Description : Validates the given tag list
82 * 1 : enabled_tags = The tags to validate
86 *********************************************************************/
87 static void validate_tag_list(struct client_specific_tag *enabled_tags)
89 while (enabled_tags != NULL)
91 if (enabled_tags->name == NULL)
93 assert(enabled_tags->name != NULL);
94 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
96 if (enabled_tags->next != NULL)
98 if (enabled_tags->next->prev != enabled_tags)
100 assert(enabled_tags->next->prev == enabled_tags);
101 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
104 enabled_tags = enabled_tags->next;
108 /*********************************************************************
110 * Function : validate_requested_tags
112 * Description : Validates the requested_tags list
118 *********************************************************************/
119 static jb_err validate_requested_tags()
121 struct requested_tags *requested_tag;
123 for (requested_tag = requested_tags; requested_tag != NULL;
124 requested_tag = requested_tag->next)
126 if (requested_tag->client == NULL)
128 assert(requested_tag->client != NULL);
129 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
131 validate_tag_list(requested_tag->tags);
132 if (requested_tag->next != NULL)
134 if (requested_tag->next->prev != requested_tag)
136 assert(requested_tag->next->prev == requested_tag);
137 log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
146 /*********************************************************************
148 * Function : get_client_specific_tag
150 * Description : Returns the data for a client-specific-tag specified
154 * 1 : tag_list = The tag list to check
155 * 2 : name = The tag name to look up
157 * Returns : Pointer to tag structure or NULL on error.
159 *********************************************************************/
160 static struct client_tag_spec *get_client_specific_tag(
161 struct client_tag_spec *tag_list, const char *name)
163 struct client_tag_spec *tag;
165 for (tag = tag_list; tag != NULL; tag = tag->next)
167 if (tag->name != NULL && !strcmp(tag->name, name))
173 log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
180 /*********************************************************************
182 * Function : get_tags_for_client
184 * Description : Returns the list of tags the client opted-in.
187 * 1 : client_address = Address of the client
189 * Returns : Pointer to tag structure or NULL on error.
191 *********************************************************************/
192 static struct client_specific_tag *get_tags_for_client(const char *client_address)
194 struct requested_tags *requested_tag;
196 for (requested_tag = requested_tags; requested_tag != NULL;
197 requested_tag = requested_tag->next)
199 if (!strcmp(requested_tag->client, client_address))
201 return requested_tag->tags;
209 /*********************************************************************
211 * Function : get_tag_list_for_client
213 * Description : Provides a list of tag names the client opted-in.
214 * Other tag attributes are not part of the list.
217 * 1 : tag_list = The list to fill in.
218 * 2 : client_address = Address of the client
220 * Returns : Pointer to tag list.
222 *********************************************************************/
223 void get_tag_list_for_client(struct list *tag_list,
224 const char *client_address)
226 struct client_specific_tag *enabled_tags;
227 const time_t now = time(NULL);
229 privoxy_mutex_lock(&client_tags_mutex);
231 enabled_tags = get_tags_for_client(client_address);
232 while (enabled_tags != NULL)
234 if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
236 struct client_specific_tag *next_tag = enabled_tags->next;
237 log_error(LOG_LEVEL_TAGGING,
238 "Tag '%s' for client %s expired %ld seconds ago. Deleting it.",
239 enabled_tags->name, client_address,
240 (now - enabled_tags->end_of_life));
241 remove_tag_for_client(client_address, enabled_tags->name);
242 enabled_tags = next_tag;
247 log_error(LOG_LEVEL_TAGGING, "Enlisting tag '%s' for client %s.",
248 enabled_tags->name, client_address);
249 enlist(tag_list, enabled_tags->name);
251 enabled_tags = enabled_tags->next;
254 privoxy_mutex_unlock(&client_tags_mutex);
258 /*********************************************************************
260 * Function : get_next_tag_timeout_for_client
262 * Description : Figures out when the next temporarily enabled tag
263 * for the client will have timed out.
266 * 1 : client_address = Address of the client
268 * Returns : Lowest timeout in seconds
270 *********************************************************************/
271 time_t get_next_tag_timeout_for_client(const char *client_address)
273 struct client_specific_tag *enabled_tags;
274 time_t next_timeout = 0;
275 const time_t now = time(NULL);
277 privoxy_mutex_lock(&client_tags_mutex);
279 enabled_tags = get_tags_for_client(client_address);
280 while (enabled_tags != NULL)
282 log_error(LOG_LEVEL_TAGGING,
283 "Evaluating tag '%s' for client %s. End of life %ld.",
284 enabled_tags->name, client_address, enabled_tags->end_of_life);
285 if (enabled_tags->end_of_life)
287 time_t time_left = enabled_tags->end_of_life - now;
288 /* Add a second to make sure the tag will have expired */
290 log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
291 if (next_timeout == 0 || next_timeout > time_left)
293 next_timeout = time_left;
296 enabled_tags = enabled_tags->next;
299 privoxy_mutex_unlock(&client_tags_mutex);
301 log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
308 /*********************************************************************
310 * Function : create_client_specific_tag
312 * Description : Allocates memory for a client specific tag
316 * 1 : name = The name of the tag to create.
317 * 2 : time_to_live = 0, or the number of seconds
318 * the tag remains activated.
320 * Returns : Pointer to populated tag
322 *********************************************************************/
323 static struct client_specific_tag *create_client_specific_tag(const char *name,
324 const time_t time_to_live)
326 struct client_specific_tag *tag;
328 tag = zalloc_or_die(sizeof(struct client_specific_tag));
329 tag->name = strdup_or_die(name);
330 tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
336 /*********************************************************************
338 * Function : add_tag_for_client
340 * Description : Adds the tag for the client.
343 * 1 : client_address = Address of the client
344 * 2 : tag = The tag to add.
345 * 3 : time_to_live = 0, or the number of seconds
346 * the tag remains activated.
350 *********************************************************************/
351 static void add_tag_for_client(const char *client_address,
352 const char *tag, const time_t time_to_live)
354 struct requested_tags *clients_with_tags;
355 struct client_specific_tag *enabled_tags;
357 validate_requested_tags();
359 if (requested_tags == NULL)
361 /* XXX: Code duplication. */
362 requested_tags = zalloc_or_die(sizeof(struct requested_tags));
363 requested_tags->client = strdup_or_die(client_address);
364 requested_tags->tags = create_client_specific_tag(tag, time_to_live);
366 validate_requested_tags();
371 clients_with_tags = requested_tags;
372 while (clients_with_tags->next != NULL)
374 if (!strcmp(clients_with_tags->client, client_address))
378 clients_with_tags = clients_with_tags->next;
380 if (strcmp(clients_with_tags->client, client_address))
382 /* Client does not have tags yet, add new structure */
383 clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
384 clients_with_tags->next->prev = clients_with_tags;
385 clients_with_tags = clients_with_tags->next;
386 clients_with_tags->client = strdup_or_die(client_address);
387 clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
389 validate_requested_tags();
395 enabled_tags = clients_with_tags->tags;
396 while (enabled_tags != NULL)
398 if (enabled_tags->next == NULL)
400 enabled_tags->next = create_client_specific_tag(tag, time_to_live);
401 enabled_tags->next->prev = enabled_tags;
404 enabled_tags = enabled_tags->next;
407 validate_requested_tags();
411 /*********************************************************************
413 * Function : remove_tag_for_client
415 * Description : Removes the tag for the client.
418 * 1 : client_address = Address of the client
419 * 2 : tag = The tag to remove.
423 *********************************************************************/
424 static void remove_tag_for_client(const char *client_address, const char *tag)
426 struct requested_tags *clients_with_tags;
427 struct client_specific_tag *enabled_tags;
429 validate_requested_tags();
431 clients_with_tags = requested_tags;
432 while (clients_with_tags != NULL && clients_with_tags->client != NULL)
434 if (!strcmp(clients_with_tags->client, client_address))
438 clients_with_tags = clients_with_tags->next;
441 assert(clients_with_tags != NULL);
442 if (clients_with_tags == NULL)
444 log_error(LOG_LEVEL_ERROR,
445 "Tried to remove tag %s for tag-less client %s",
446 tag, client_address);
448 enabled_tags = clients_with_tags->tags;
449 while (enabled_tags != NULL)
451 if (!strcmp(enabled_tags->name, tag))
453 if (enabled_tags->next != NULL)
455 enabled_tags->next->prev = enabled_tags->prev;
456 if (enabled_tags == clients_with_tags->tags)
458 /* Tag is first in line */
459 clients_with_tags->tags = enabled_tags->next;
462 if (enabled_tags->prev != NULL)
464 /* Tag has preceding tag */
465 enabled_tags->prev->next = enabled_tags->next;
467 if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
469 /* Tag is the only one */
470 if (clients_with_tags->next != NULL)
472 /* Client has following client */
473 clients_with_tags->next->prev = clients_with_tags->prev;
475 if (clients_with_tags->prev != NULL)
477 /* Client has preceding client */
478 clients_with_tags->prev->next = clients_with_tags->next;
480 if (clients_with_tags == requested_tags)
483 * We're in the process of removing the last tag,
484 * mark the global list as empty.
486 requested_tags = NULL;
488 freez(clients_with_tags->client);
489 freez(clients_with_tags);
491 freez(enabled_tags->name);
496 enabled_tags = enabled_tags->next;
499 validate_requested_tags();
504 /*********************************************************************
506 * Function : client_has_requested_tag
508 * Description : Checks whether or not the given client requested
512 * 1 : client_address = Address of the client
513 * 2 : tag = Tag to check.
515 * Returns : TRUE or FALSE.
517 *********************************************************************/
518 int client_has_requested_tag(const char *client_address, const char *tag)
520 struct client_specific_tag *enabled_tags;
522 enabled_tags = get_tags_for_client(client_address);
524 while (enabled_tags != NULL)
526 if (!strcmp(enabled_tags->name, tag))
530 enabled_tags = enabled_tags->next;
537 /*********************************************************************
539 * Function : enable_client_specific_tag
541 * Description : Enables a client-specific-tag for the client
544 * 1 : csp = Current client state (buffers, headers, etc...)
545 * 2 : tag_name = The name of the tag to enable
546 * 3 : time_to_live = If not 0, the number of seconds the
547 * tag should stay enabled.
549 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
551 *********************************************************************/
552 jb_err enable_client_specific_tag(struct client_state *csp,
553 const char *tag_name, const time_t time_to_live)
555 struct client_tag_spec *tag;
557 privoxy_mutex_lock(&client_tags_mutex);
559 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
562 privoxy_mutex_unlock(&client_tags_mutex);
566 if (client_has_requested_tag(csp->client_address, tag_name))
568 log_error(LOG_LEVEL_TAGGING,
569 "Tag '%s' already enabled for client '%s'.",
570 tag->name, csp->client_address);
574 add_tag_for_client(csp->client_address, tag_name, time_to_live);
575 log_error(LOG_LEVEL_TAGGING,
576 "Tag '%s' enabled for client '%s'. TTL: %ld.",
577 tag->name, csp->client_address, time_to_live);
580 privoxy_mutex_unlock(&client_tags_mutex);
586 /*********************************************************************
588 * Function : disable_client_specific_tag
590 * Description : Disables a client-specific-tag for the client
593 * 1 : csp = Current client state (buffers, headers, etc...)
594 * 2 : tag_name = The name of the tag to disable
596 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
598 *********************************************************************/
599 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
601 struct client_tag_spec *tag;
603 privoxy_mutex_lock(&client_tags_mutex);
605 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
608 privoxy_mutex_unlock(&client_tags_mutex);
612 if (client_has_requested_tag(csp->client_address, tag_name))
614 remove_tag_for_client(csp->client_address, tag_name);
615 log_error(LOG_LEVEL_TAGGING,
616 "Tag '%s' disabled for client '%s'.", tag->name, csp->client_address);
620 log_error(LOG_LEVEL_TAGGING,
621 "Tag '%s' currently not set for client '%s'.",
622 tag->name, csp->client_address);
625 privoxy_mutex_unlock(&client_tags_mutex);
631 /*********************************************************************
633 * Function : client_tag_match
635 * Description : Compare a client tag against a client tag pattern.
638 * 1 : pattern = a TAG pattern
639 * 2 : tag = Client tag to match
641 * Returns : Nonzero if the tag matches the pattern, else 0.
643 *********************************************************************/
644 int client_tag_match(const struct pattern_spec *pattern,
645 const struct list *tags)
647 struct list_entry *tag;
649 if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
652 * It's not a client pattern and thus shouldn't
653 * be matched against client tags.
660 for (tag = tags->first; tag != NULL; tag = tag->next)
663 if (pcre2_pattern_matches(pattern->pattern.tag_regex, tag->str))
665 if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
668 log_error(LOG_LEVEL_TAGGING, "Client tag '%s' matches.", tag->str);
678 /*********************************************************************
680 * Function : set_client_address
682 * Description : Sets the client address that will be used to enable,
683 * disable, or apply client tags.
686 * 1 : csp = Current client state (buffers, headers, etc...)
687 * 2 : headers = Client headers
691 *********************************************************************/
692 void set_client_address(struct client_state *csp, const struct list *headers)
694 if (csp->config->trust_x_forwarded_for)
696 const char *client_address;
698 client_address = get_header_value(headers, "X-Forwarded-For:");
699 if (client_address != NULL)
701 csp->client_address = strdup_or_die(client_address);
702 log_error(LOG_LEVEL_HEADER,
703 "Got client address %s from X-Forwarded-For header",
704 csp->client_address);
708 if (csp->client_address == NULL)
710 csp->client_address = strdup_or_die(csp->ip_addr_str);
715 #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
716 #endif /* def FEATURE_CLIENT_TAGS */