123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- /*********************************************************************
- *
- * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
- *
- * Purpose : Functions related to client-specific tags.
- *
- * Copyright : Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
- *
- * This program is free software; you can redistribute it
- * and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will
- * be useful, but WITHOUT ANY WARRANTY; without even the
- * implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU General Public
- * License for more details.
- *
- * The GNU General Public License should be included with
- * this file. If not, you can view it at
- * http://www.gnu.org/copyleft/gpl.html
- * or write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- **********************************************************************/
- #include "config.h"
- #ifdef FEATURE_CLIENT_TAGS
- #include <stdio.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <assert.h>
- #include "project.h"
- #include "list.h"
- #include "jcc.h"
- #include "miscutil.h"
- #include "errlog.h"
- #include "parsers.h"
- struct client_specific_tag
- {
- char *name;
- time_t end_of_life;
- struct client_specific_tag *next;
- struct client_specific_tag *prev;
- };
- /**
- * This struct represents tags that have been requested by clients
- */
- struct requested_tags
- {
- char *client; /**< The IP address of the client that requested the tag */
- /**< List of tags the client requested .... */
- struct client_specific_tag *tags;
- struct requested_tags *next;
- struct requested_tags *prev;
- };
- struct requested_tags *requested_tags;
- static void remove_tag_for_client(const char *client_address, const char *tag);
- /*********************************************************************
- *
- * Function : validate_tag_list
- *
- * Description : Validates the given tag list
- *
- * Parameters :
- * 1 : enabled_tags = The tags to validate
- *
- * Returns : void
- *
- *********************************************************************/
- static void validate_tag_list(struct client_specific_tag *enabled_tags)
- {
- while (enabled_tags != NULL)
- {
- if (enabled_tags->name == NULL)
- {
- assert(enabled_tags->name != NULL);
- log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
- }
- if (enabled_tags->next != NULL)
- {
- if (enabled_tags->next->prev != enabled_tags)
- {
- assert(enabled_tags->next->prev == enabled_tags);
- log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
- }
- }
- enabled_tags = enabled_tags->next;
- }
- }
- /*********************************************************************
- *
- * Function : validate_requested_tags
- *
- * Description : Validates the requested_tags list
- *
- * Parameters : N/A
- *
- * Returns : void
- *
- *********************************************************************/
- static jb_err validate_requested_tags()
- {
- struct requested_tags *requested_tag;
- for (requested_tag = requested_tags; requested_tag != NULL;
- requested_tag = requested_tag->next)
- {
- if (requested_tag->client == NULL)
- {
- assert(requested_tag->client != NULL);
- log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
- }
- validate_tag_list(requested_tag->tags);
- if (requested_tag->next != NULL)
- {
- if (requested_tag->next->prev != requested_tag)
- {
- assert(requested_tag->next->prev == requested_tag);
- log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
- }
- }
- }
- return TRUE;
- }
- /*********************************************************************
- *
- * Function : get_client_specific_tag
- *
- * Description : Returns the data for a client-specific-tag specified
- * by name.
- *
- * Parameters :
- * 1 : tag_list = The tag list to check
- * 2 : name = The tag name to look up
- *
- * Returns : Pointer to tag structure or NULL on error.
- *
- *********************************************************************/
- static struct client_tag_spec *get_client_specific_tag(
- struct client_tag_spec *tag_list, const char *name)
- {
- struct client_tag_spec *tag;
- for (tag = tag_list; tag != NULL; tag = tag->next)
- {
- if (tag->name != NULL && !strcmp(tag->name, name))
- {
- return tag;
- }
- }
- log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
- return NULL;
- }
- /*********************************************************************
- *
- * Function : get_tags_for_client
- *
- * Description : Returns the list of tags the client opted-in.
- *
- * Parameters :
- * 1 : client_address = Address of the client
- *
- * Returns : Pointer to tag structure or NULL on error.
- *
- *********************************************************************/
- static struct client_specific_tag *get_tags_for_client(const char *client_address)
- {
- struct requested_tags *requested_tag;
- for (requested_tag = requested_tags; requested_tag != NULL;
- requested_tag = requested_tag->next)
- {
- if (!strcmp(requested_tag->client, client_address))
- {
- return requested_tag->tags;
- }
- }
- return NULL;
- }
- /*********************************************************************
- *
- * Function : get_tag_list_for_client
- *
- * Description : Provides a list of tag names the client opted-in.
- * Other tag attributes are not part of the list.
- *
- * Parameters :
- * 1 : tag_list = The list to fill in.
- * 2 : client_address = Address of the client
- *
- * Returns : Pointer to tag list.
- *
- *********************************************************************/
- void get_tag_list_for_client(struct list *tag_list,
- const char *client_address)
- {
- struct client_specific_tag *enabled_tags;
- const time_t now = time(NULL);
- privoxy_mutex_lock(&client_tags_mutex);
- enabled_tags = get_tags_for_client(client_address);
- while (enabled_tags != NULL)
- {
- if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
- {
- struct client_specific_tag *next_tag = enabled_tags->next;
- log_error(LOG_LEVEL_INFO,
- "Tag '%s' for client %s expired %ld seconds ago. Deleting it.",
- enabled_tags->name, client_address,
- (now - enabled_tags->end_of_life));
- remove_tag_for_client(client_address, enabled_tags->name);
- enabled_tags = next_tag;
- continue;
- }
- else
- {
- enlist(tag_list, enabled_tags->name);
- }
- enabled_tags = enabled_tags->next;
- }
- privoxy_mutex_unlock(&client_tags_mutex);
- }
- /*********************************************************************
- *
- * Function : get_next_tag_timeout_for_client
- *
- * Description : Figures out when the next temporarily enabled tag
- * for the client will have timed out.
- *
- * Parameters :
- * 1 : client_address = Address of the client
- *
- * Returns : Lowest timeout in seconds
- *
- *********************************************************************/
- time_t get_next_tag_timeout_for_client(const char *client_address)
- {
- struct client_specific_tag *enabled_tags;
- time_t next_timeout = 0;
- const time_t now = time(NULL);
- privoxy_mutex_lock(&client_tags_mutex);
- enabled_tags = get_tags_for_client(client_address);
- while (enabled_tags != NULL)
- {
- log_error(LOG_LEVEL_CGI, "Evaluating tag '%s' for client %s. End of life %ld",
- enabled_tags->name, client_address, enabled_tags->end_of_life);
- if (enabled_tags->end_of_life)
- {
- time_t time_left = enabled_tags->end_of_life - now;
- /* Add a second to make sure the tag will have expired */
- time_left++;
- log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
- if (next_timeout == 0 || next_timeout > time_left)
- {
- next_timeout = time_left;
- }
- }
- enabled_tags = enabled_tags->next;
- }
- privoxy_mutex_unlock(&client_tags_mutex);
- log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
- return next_timeout;
- }
- /*********************************************************************
- *
- * Function : create_client_specific_tag
- *
- * Description : Allocates memory for a client specific tag
- * and populates it.
- *
- * Parameters :
- * 1 : name = The name of the tag to create.
- * 2 : time_to_live = 0, or the number of seconds
- * the tag remains activated.
- *
- * Returns : Pointer to populated tag
- *
- *********************************************************************/
- static struct client_specific_tag *create_client_specific_tag(const char *name,
- const time_t time_to_live)
- {
- struct client_specific_tag *tag;
- tag = zalloc_or_die(sizeof(struct client_specific_tag));
- tag->name = strdup_or_die(name);
- tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
- return tag;
- }
- /*********************************************************************
- *
- * Function : add_tag_for_client
- *
- * Description : Adds the tag for the client.
- *
- * Parameters :
- * 1 : client_address = Address of the client
- * 2 : tag = The tag to add.
- * 3 : time_to_live = 0, or the number of seconds
- * the tag remains activated.
- *
- * Returns : void
- *
- *********************************************************************/
- static void add_tag_for_client(const char *client_address,
- const char *tag, const time_t time_to_live)
- {
- struct requested_tags *clients_with_tags;
- struct client_specific_tag *enabled_tags;
- validate_requested_tags();
- if (requested_tags == NULL)
- {
- /* XXX: Code duplication. */
- requested_tags = zalloc_or_die(sizeof(struct requested_tags));
- requested_tags->client = strdup_or_die(client_address);
- requested_tags->tags = create_client_specific_tag(tag, time_to_live);
- validate_requested_tags();
- return;
- }
- else
- {
- clients_with_tags = requested_tags;
- while (clients_with_tags->next != NULL)
- {
- if (!strcmp(clients_with_tags->client, client_address))
- {
- break;
- }
- clients_with_tags = clients_with_tags->next;
- }
- if (strcmp(clients_with_tags->client, client_address))
- {
- /* Client does not have tags yet, add new structure */
- clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
- clients_with_tags->next->prev = clients_with_tags;
- clients_with_tags = clients_with_tags->next;
- clients_with_tags->client = strdup_or_die(client_address);
- clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
- validate_requested_tags();
- return;
- }
- }
- enabled_tags = clients_with_tags->tags;
- while (enabled_tags != NULL)
- {
- if (enabled_tags->next == NULL)
- {
- enabled_tags->next = create_client_specific_tag(tag, time_to_live);
- enabled_tags->next->prev = enabled_tags;
- break;
- }
- enabled_tags = enabled_tags->next;
- }
- validate_requested_tags();
- }
- /*********************************************************************
- *
- * Function : remove_tag_for_client
- *
- * Description : Removes the tag for the client.
- *
- * Parameters :
- * 1 : client_address = Address of the client
- * 2 : tag = The tag to remove.
- *
- * Returns : void
- *
- *********************************************************************/
- static void remove_tag_for_client(const char *client_address, const char *tag)
- {
- struct requested_tags *clients_with_tags;
- struct client_specific_tag *enabled_tags;
- validate_requested_tags();
- clients_with_tags = requested_tags;
- while (clients_with_tags != NULL && clients_with_tags->client != NULL)
- {
- if (!strcmp(clients_with_tags->client, client_address))
- {
- break;
- }
- clients_with_tags = clients_with_tags->next;
- }
- assert(clients_with_tags != NULL);
- if (clients_with_tags == NULL)
- {
- log_error(LOG_LEVEL_ERROR,
- "Tried to remove tag %s for tag-less client %s",
- tag, client_address);
- }
- enabled_tags = clients_with_tags->tags;
- while (enabled_tags != NULL)
- {
- if (!strcmp(enabled_tags->name, tag))
- {
- if (enabled_tags->next != NULL)
- {
- enabled_tags->next->prev = enabled_tags->prev;
- if (enabled_tags == clients_with_tags->tags)
- {
- /* Tag is first in line */
- clients_with_tags->tags = enabled_tags->next;
- }
- }
- if (enabled_tags->prev != NULL)
- {
- /* Tag has preceding tag */
- enabled_tags->prev->next = enabled_tags->next;
- }
- if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
- {
- /* Tag is the only one */
- if (clients_with_tags->next != NULL)
- {
- /* Client has following client */
- clients_with_tags->next->prev = clients_with_tags->prev;
- }
- if (clients_with_tags->prev != NULL)
- {
- /* Client has preceding client */
- clients_with_tags->prev->next = clients_with_tags->next;
- }
- if (clients_with_tags == requested_tags)
- {
- /*
- * We're in the process of removing the last tag,
- * mark the global list as empty.
- */
- requested_tags = NULL;
- }
- freez(clients_with_tags->client);
- freez(clients_with_tags);
- }
- freez(enabled_tags->name);
- freez(enabled_tags);
- break;
- }
- enabled_tags = enabled_tags->next;
- }
- validate_requested_tags();
- }
- /*********************************************************************
- *
- * Function : client_has_requested_tag
- *
- * Description : Checks whether or not the given client requested
- * the tag.
- *
- * Parameters :
- * 1 : client_address = Address of the client
- * 2 : tag = Tag to check.
- *
- * Returns : TRUE or FALSE.
- *
- *********************************************************************/
- int client_has_requested_tag(const char *client_address, const char *tag)
- {
- struct client_specific_tag *enabled_tags;
- enabled_tags = get_tags_for_client(client_address);
- while (enabled_tags != NULL)
- {
- if (!strcmp(enabled_tags->name, tag))
- {
- return TRUE;
- }
- enabled_tags = enabled_tags->next;
- }
- return FALSE;
- }
- /*********************************************************************
- *
- * Function : enable_client_specific_tag
- *
- * Description : Enables a client-specific-tag for the client
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- * 2 : tag_name = The name of the tag to enable
- * 3 : time_to_live = If not 0, the number of seconds the
- * tag should stay enabled.
- *
- * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
- *
- *********************************************************************/
- jb_err enable_client_specific_tag(struct client_state *csp,
- const char *tag_name, const time_t time_to_live)
- {
- struct client_tag_spec *tag;
- privoxy_mutex_lock(&client_tags_mutex);
- tag = get_client_specific_tag(csp->config->client_tags, tag_name);
- if (tag == NULL)
- {
- privoxy_mutex_unlock(&client_tags_mutex);
- return JB_ERR_PARSE;
- }
- if (client_has_requested_tag(csp->client_address, tag_name))
- {
- log_error(LOG_LEVEL_ERROR,
- "Tag '%s' already enabled for client '%s'", tag->name, csp->client_address);
- }
- else
- {
- add_tag_for_client(csp->client_address, tag_name, time_to_live);
- log_error(LOG_LEVEL_INFO,
- "Tag '%s' enabled for client '%s'. TTL: %ld.",
- tag->name, csp->client_address, time_to_live);
- }
- privoxy_mutex_unlock(&client_tags_mutex);
- return JB_ERR_OK;
- }
- /*********************************************************************
- *
- * Function : disable_client_specific_tag
- *
- * Description : Disables a client-specific-tag for the client
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- * 2 : tag_name = The name of the tag to disable
- *
- * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
- *
- *********************************************************************/
- jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
- {
- struct client_tag_spec *tag;
- privoxy_mutex_lock(&client_tags_mutex);
- tag = get_client_specific_tag(csp->config->client_tags, tag_name);
- if (tag == NULL)
- {
- privoxy_mutex_unlock(&client_tags_mutex);
- return JB_ERR_PARSE;
- }
- if (client_has_requested_tag(csp->client_address, tag_name))
- {
- remove_tag_for_client(csp->client_address, tag_name);
- log_error(LOG_LEVEL_INFO,
- "Tag '%s' disabled for client '%s'", tag->name, csp->client_address);
- }
- else
- {
- log_error(LOG_LEVEL_ERROR,
- "Tag '%s' currently not set for client '%s'",
- tag->name, csp->client_address);
- }
- privoxy_mutex_unlock(&client_tags_mutex);
- return JB_ERR_OK;
- }
- /*********************************************************************
- *
- * Function : client_tag_match
- *
- * Description : Compare a client tag against a client tag pattern.
- *
- * Parameters :
- * 1 : pattern = a TAG pattern
- * 2 : tag = Client tag to match
- *
- * Returns : Nonzero if the tag matches the pattern, else 0.
- *
- *********************************************************************/
- int client_tag_match(const struct pattern_spec *pattern,
- const struct list *tags)
- {
- struct list_entry *tag;
- if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
- {
- /*
- * It's not a client pattern and thus shouldn't
- * be matched against client tags.
- */
- return 0;
- }
- assert(tags);
- for (tag = tags->first; tag != NULL; tag = tag->next)
- {
- if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
- {
- return 1;
- }
- }
- return 0;
- }
- /*********************************************************************
- *
- * Function : set_client_address
- *
- * Description : Sets the client address that will be used to enable,
- * disable, or apply client tags.
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- * 2 : headers = Client headers
- *
- * Returns : void.
- *
- *********************************************************************/
- void set_client_address(struct client_state *csp, const struct list *headers)
- {
- if (csp->config->trust_x_forwarded_for)
- {
- const char *client_address;
- client_address = get_header_value(headers, "X-Forwarded-For:");
- if (client_address != NULL)
- {
- csp->client_address = strdup_or_die(client_address);
- log_error(LOG_LEVEL_HEADER,
- "Got client address %s from X-Forwarded-For header",
- csp->client_address);
- }
- }
- if (csp->client_address == NULL)
- {
- csp->client_address = strdup_or_die(csp->ip_addr_str);
- }
- }
- #else
- #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
- #endif /* def FEATURE_CLIENT_TAGS */
|