Line data Source code
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 :
29 : #include "config.h"
30 :
31 : #ifdef FEATURE_CLIENT_TAGS
32 :
33 : #include <stdio.h>
34 : #include <sys/types.h>
35 : #include <stdlib.h>
36 : #include <ctype.h>
37 : #include <string.h>
38 : #include <assert.h>
39 :
40 : #include "project.h"
41 : #include "list.h"
42 : #include "jcc.h"
43 : #include "miscutil.h"
44 : #include "errlog.h"
45 : #include "parsers.h"
46 :
47 : struct client_specific_tag
48 : {
49 : char *name;
50 :
51 : time_t end_of_life;
52 :
53 : struct client_specific_tag *next;
54 : struct client_specific_tag *prev;
55 : };
56 :
57 : /**
58 : * This struct represents tags that have been requested by clients
59 : */
60 : struct requested_tags
61 : {
62 : char *client; /**< The IP address of the client that requested the tag */
63 :
64 : /**< List of tags the client requested .... */
65 : struct client_specific_tag *tags;
66 :
67 : struct requested_tags *next;
68 : struct requested_tags *prev;
69 : };
70 :
71 : struct requested_tags *requested_tags;
72 : static void remove_tag_for_client(const char *client_address, const char *tag);
73 :
74 : /*********************************************************************
75 : *
76 : * Function : validate_tag_list
77 : *
78 : * Description : Validates the given tag list
79 : *
80 : * Parameters :
81 : * 1 : enabled_tags = The tags to validate
82 : *
83 : * Returns : void
84 : *
85 : *********************************************************************/
86 1969 : static void validate_tag_list(struct client_specific_tag *enabled_tags)
87 : {
88 3938 : while (enabled_tags != NULL)
89 : {
90 1969 : if (enabled_tags->name == NULL)
91 : {
92 0 : assert(enabled_tags->name != NULL);
93 0 : log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
94 : }
95 1969 : if (enabled_tags->next != NULL)
96 : {
97 0 : if (enabled_tags->next->prev != enabled_tags)
98 : {
99 0 : assert(enabled_tags->next->prev == enabled_tags);
100 0 : log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
101 : }
102 : }
103 1969 : enabled_tags = enabled_tags->next;
104 : }
105 1969 : }
106 :
107 : /*********************************************************************
108 : *
109 : * Function : validate_requested_tags
110 : *
111 : * Description : Validates the requested_tags list
112 : *
113 : * Parameters : N/A
114 : *
115 : * Returns : void
116 : *
117 : *********************************************************************/
118 832 : static jb_err validate_requested_tags()
119 : {
120 : struct requested_tags *requested_tag;
121 :
122 2801 : for (requested_tag = requested_tags; requested_tag != NULL;
123 1969 : requested_tag = requested_tag->next)
124 : {
125 1969 : if (requested_tag->client == NULL)
126 : {
127 0 : assert(requested_tag->client != NULL);
128 0 : log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
129 : }
130 1969 : validate_tag_list(requested_tag->tags);
131 1969 : if (requested_tag->next != NULL)
132 : {
133 1314 : if (requested_tag->next->prev != requested_tag)
134 : {
135 0 : assert(requested_tag->next->prev == requested_tag);
136 0 : log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
137 : }
138 : }
139 : }
140 :
141 832 : return TRUE;
142 : }
143 :
144 :
145 : /*********************************************************************
146 : *
147 : * Function : get_client_specific_tag
148 : *
149 : * Description : Returns the data for a client-specific-tag specified
150 : * by name.
151 : *
152 : * Parameters :
153 : * 1 : tag_list = The tag list to check
154 : * 2 : name = The tag name to look up
155 : *
156 : * Returns : Pointer to tag structure or NULL on error.
157 : *
158 : *********************************************************************/
159 805 : static struct client_tag_spec *get_client_specific_tag(
160 : struct client_tag_spec *tag_list, const char *name)
161 : {
162 : struct client_tag_spec *tag;
163 :
164 1796 : for (tag = tag_list; tag != NULL; tag = tag->next)
165 : {
166 1734 : if (tag->name != NULL && !strcmp(tag->name, name))
167 : {
168 743 : return tag;
169 : }
170 : }
171 :
172 62 : log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
173 :
174 62 : return NULL;
175 :
176 : }
177 :
178 :
179 : /*********************************************************************
180 : *
181 : * Function : get_tags_for_client
182 : *
183 : * Description : Returns the list of tags the client opted-in.
184 : *
185 : * Parameters :
186 : * 1 : client_address = Address of the client
187 : *
188 : * Returns : Pointer to tag structure or NULL on error.
189 : *
190 : *********************************************************************/
191 34918 : static struct client_specific_tag *get_tags_for_client(const char *client_address)
192 : {
193 : struct requested_tags *requested_tag;
194 :
195 38224 : for (requested_tag = requested_tags; requested_tag != NULL;
196 3306 : requested_tag = requested_tag->next)
197 : {
198 4850 : if (!strcmp(requested_tag->client, client_address))
199 : {
200 1544 : return requested_tag->tags;
201 : }
202 : }
203 :
204 33374 : return NULL;
205 : }
206 :
207 :
208 : /*********************************************************************
209 : *
210 : * Function : get_tag_list_for_client
211 : *
212 : * Description : Provides a list of tag names the client opted-in.
213 : * Other tag attributes are not part of the list.
214 : *
215 : * Parameters :
216 : * 1 : tag_list = The list to fill in.
217 : * 2 : client_address = Address of the client
218 : *
219 : * Returns : Pointer to tag list.
220 : *
221 : *********************************************************************/
222 33010 : void get_tag_list_for_client(struct list *tag_list,
223 : const char *client_address)
224 : {
225 : struct client_specific_tag *enabled_tags;
226 33010 : const time_t now = time(NULL);
227 :
228 33010 : privoxy_mutex_lock(&client_tags_mutex);
229 :
230 33010 : enabled_tags = get_tags_for_client(client_address);
231 33799 : while (enabled_tags != NULL)
232 : {
233 789 : if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
234 : {
235 0 : struct client_specific_tag *next_tag = enabled_tags->next;
236 0 : log_error(LOG_LEVEL_TAGGING,
237 : "Tag '%s' for client %s expired %ld seconds ago. Deleting it.",
238 : enabled_tags->name, client_address,
239 0 : (now - enabled_tags->end_of_life));
240 0 : remove_tag_for_client(client_address, enabled_tags->name);
241 0 : enabled_tags = next_tag;
242 0 : continue;
243 : }
244 : else
245 : {
246 789 : log_error(LOG_LEVEL_TAGGING, "Enlisting tag '%s' for client %s.",
247 : enabled_tags->name, client_address);
248 789 : enlist(tag_list, enabled_tags->name);
249 : }
250 789 : enabled_tags = enabled_tags->next;
251 : }
252 :
253 33010 : privoxy_mutex_unlock(&client_tags_mutex);
254 33010 : }
255 :
256 :
257 : /*********************************************************************
258 : *
259 : * Function : get_next_tag_timeout_for_client
260 : *
261 : * Description : Figures out when the next temporarily enabled tag
262 : * for the client will have timed out.
263 : *
264 : * Parameters :
265 : * 1 : client_address = Address of the client
266 : *
267 : * Returns : Lowest timeout in seconds
268 : *
269 : *********************************************************************/
270 233 : time_t get_next_tag_timeout_for_client(const char *client_address)
271 : {
272 : struct client_specific_tag *enabled_tags;
273 233 : time_t next_timeout = 0;
274 233 : const time_t now = time(NULL);
275 :
276 233 : privoxy_mutex_lock(&client_tags_mutex);
277 :
278 233 : enabled_tags = get_tags_for_client(client_address);
279 316 : while (enabled_tags != NULL)
280 : {
281 83 : log_error(LOG_LEVEL_TAGGING,
282 : "Evaluating tag '%s' for client %s. End of life %ld.",
283 : enabled_tags->name, client_address, enabled_tags->end_of_life);
284 83 : if (enabled_tags->end_of_life)
285 : {
286 47 : time_t time_left = enabled_tags->end_of_life - now;
287 : /* Add a second to make sure the tag will have expired */
288 47 : time_left++;
289 47 : log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
290 47 : if (next_timeout == 0 || next_timeout > time_left)
291 : {
292 47 : next_timeout = time_left;
293 : }
294 : }
295 83 : enabled_tags = enabled_tags->next;
296 : }
297 :
298 233 : privoxy_mutex_unlock(&client_tags_mutex);
299 :
300 233 : log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
301 :
302 233 : return next_timeout;
303 :
304 : }
305 :
306 :
307 : /*********************************************************************
308 : *
309 : * Function : create_client_specific_tag
310 : *
311 : * Description : Allocates memory for a client specific tag
312 : * and populates it.
313 : *
314 : * Parameters :
315 : * 1 : name = The name of the tag to create.
316 : * 2 : time_to_live = 0, or the number of seconds
317 : * the tag remains activated.
318 : *
319 : * Returns : Pointer to populated tag
320 : *
321 : *********************************************************************/
322 284 : static struct client_specific_tag *create_client_specific_tag(const char *name,
323 : const time_t time_to_live)
324 : {
325 : struct client_specific_tag *tag;
326 :
327 284 : tag = zalloc_or_die(sizeof(struct client_specific_tag));
328 284 : tag->name = strdup_or_die(name);
329 284 : tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
330 :
331 284 : return tag;
332 :
333 : }
334 :
335 : /*********************************************************************
336 : *
337 : * Function : add_tag_for_client
338 : *
339 : * Description : Adds the tag for the client.
340 : *
341 : * Parameters :
342 : * 1 : client_address = Address of the client
343 : * 2 : tag = The tag to add.
344 : * 3 : time_to_live = 0, or the number of seconds
345 : * the tag remains activated.
346 : *
347 : * Returns : void
348 : *
349 : *********************************************************************/
350 284 : static void add_tag_for_client(const char *client_address,
351 : const char *tag, const time_t time_to_live)
352 : {
353 : struct requested_tags *clients_with_tags;
354 : struct client_specific_tag *enabled_tags;
355 :
356 284 : validate_requested_tags();
357 :
358 284 : if (requested_tags == NULL)
359 : {
360 : /* XXX: Code duplication. */
361 119 : requested_tags = zalloc_or_die(sizeof(struct requested_tags));
362 119 : requested_tags->client = strdup_or_die(client_address);
363 119 : requested_tags->tags = create_client_specific_tag(tag, time_to_live);
364 :
365 119 : validate_requested_tags();
366 119 : return;
367 : }
368 : else
369 : {
370 165 : clients_with_tags = requested_tags;
371 482 : while (clients_with_tags->next != NULL)
372 : {
373 317 : if (!strcmp(clients_with_tags->client, client_address))
374 : {
375 0 : break;
376 : }
377 317 : clients_with_tags = clients_with_tags->next;
378 : }
379 165 : if (strcmp(clients_with_tags->client, client_address))
380 : {
381 : /* Client does not have tags yet, add new structure */
382 165 : clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
383 165 : clients_with_tags->next->prev = clients_with_tags;
384 165 : clients_with_tags = clients_with_tags->next;
385 165 : clients_with_tags->client = strdup_or_die(client_address);
386 165 : clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
387 :
388 165 : validate_requested_tags();
389 :
390 165 : return;
391 : }
392 : }
393 :
394 0 : enabled_tags = clients_with_tags->tags;
395 0 : while (enabled_tags != NULL)
396 : {
397 0 : if (enabled_tags->next == NULL)
398 : {
399 0 : enabled_tags->next = create_client_specific_tag(tag, time_to_live);
400 0 : enabled_tags->next->prev = enabled_tags;
401 0 : break;
402 : }
403 0 : enabled_tags = enabled_tags->next;
404 : }
405 :
406 0 : validate_requested_tags();
407 : }
408 :
409 :
410 : /*********************************************************************
411 : *
412 : * Function : remove_tag_for_client
413 : *
414 : * Description : Removes the tag for the client.
415 : *
416 : * Parameters :
417 : * 1 : client_address = Address of the client
418 : * 2 : tag = The tag to remove.
419 : *
420 : * Returns : void
421 : *
422 : *********************************************************************/
423 132 : static void remove_tag_for_client(const char *client_address, const char *tag)
424 : {
425 : struct requested_tags *clients_with_tags;
426 : struct client_specific_tag *enabled_tags;
427 :
428 132 : validate_requested_tags();
429 :
430 132 : clients_with_tags = requested_tags;
431 393 : while (clients_with_tags != NULL && clients_with_tags->client != NULL)
432 : {
433 393 : if (!strcmp(clients_with_tags->client, client_address))
434 : {
435 132 : break;
436 : }
437 261 : clients_with_tags = clients_with_tags->next;
438 : }
439 :
440 132 : assert(clients_with_tags != NULL);
441 132 : if (clients_with_tags == NULL)
442 : {
443 0 : log_error(LOG_LEVEL_ERROR,
444 : "Tried to remove tag %s for tag-less client %s",
445 : tag, client_address);
446 : }
447 132 : enabled_tags = clients_with_tags->tags;
448 132 : while (enabled_tags != NULL)
449 : {
450 132 : if (!strcmp(enabled_tags->name, tag))
451 : {
452 132 : if (enabled_tags->next != NULL)
453 : {
454 0 : enabled_tags->next->prev = enabled_tags->prev;
455 0 : if (enabled_tags == clients_with_tags->tags)
456 : {
457 : /* Tag is first in line */
458 0 : clients_with_tags->tags = enabled_tags->next;
459 : }
460 : }
461 132 : if (enabled_tags->prev != NULL)
462 : {
463 : /* Tag has preceding tag */
464 0 : enabled_tags->prev->next = enabled_tags->next;
465 : }
466 132 : if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
467 : {
468 : /* Tag is the only one */
469 132 : if (clients_with_tags->next != NULL)
470 : {
471 : /* Client has following client */
472 35 : clients_with_tags->next->prev = clients_with_tags->prev;
473 : }
474 132 : if (clients_with_tags->prev != NULL)
475 : {
476 : /* Client has preceding client */
477 74 : clients_with_tags->prev->next = clients_with_tags->next;
478 : }
479 132 : if (clients_with_tags == requested_tags)
480 : {
481 : /*
482 : * We're in the process of removing the last tag,
483 : * mark the global list as empty.
484 : */
485 58 : requested_tags = NULL;
486 : }
487 132 : freez(clients_with_tags->client);
488 132 : freez(clients_with_tags);
489 : }
490 132 : freez(enabled_tags->name);
491 132 : freez(enabled_tags);
492 132 : break;
493 : }
494 :
495 0 : enabled_tags = enabled_tags->next;
496 : }
497 :
498 132 : validate_requested_tags();
499 :
500 132 : }
501 :
502 :
503 : /*********************************************************************
504 : *
505 : * Function : client_has_requested_tag
506 : *
507 : * Description : Checks whether or not the given client requested
508 : * the tag.
509 : *
510 : * Parameters :
511 : * 1 : client_address = Address of the client
512 : * 2 : tag = Tag to check.
513 : *
514 : * Returns : TRUE or FALSE.
515 : *
516 : *********************************************************************/
517 1675 : int client_has_requested_tag(const char *client_address, const char *tag)
518 : {
519 : struct client_specific_tag *enabled_tags;
520 :
521 1675 : enabled_tags = get_tags_for_client(client_address);
522 :
523 1924 : while (enabled_tags != NULL)
524 : {
525 672 : if (!strcmp(enabled_tags->name, tag))
526 : {
527 423 : return TRUE;
528 : }
529 249 : enabled_tags = enabled_tags->next;
530 : }
531 :
532 1252 : return FALSE;
533 :
534 : }
535 :
536 : /*********************************************************************
537 : *
538 : * Function : enable_client_specific_tag
539 : *
540 : * Description : Enables a client-specific-tag for the client
541 : *
542 : * Parameters :
543 : * 1 : csp = Current client state (buffers, headers, etc...)
544 : * 2 : tag_name = The name of the tag to enable
545 : * 3 : time_to_live = If not 0, the number of seconds the
546 : * tag should stay enabled.
547 : *
548 : * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
549 : *
550 : *********************************************************************/
551 511 : jb_err enable_client_specific_tag(struct client_state *csp,
552 : const char *tag_name, const time_t time_to_live)
553 : {
554 : struct client_tag_spec *tag;
555 :
556 511 : privoxy_mutex_lock(&client_tags_mutex);
557 :
558 511 : tag = get_client_specific_tag(csp->config->client_tags, tag_name);
559 511 : if (tag == NULL)
560 : {
561 19 : privoxy_mutex_unlock(&client_tags_mutex);
562 19 : return JB_ERR_PARSE;
563 : }
564 :
565 492 : if (client_has_requested_tag(csp->client_address, tag_name))
566 : {
567 208 : log_error(LOG_LEVEL_TAGGING,
568 : "Tag '%s' already enabled for client '%s'.",
569 : tag->name, csp->client_address);
570 : }
571 : else
572 : {
573 284 : add_tag_for_client(csp->client_address, tag_name, time_to_live);
574 284 : log_error(LOG_LEVEL_TAGGING,
575 : "Tag '%s' enabled for client '%s'. TTL: %ld.",
576 : tag->name, csp->client_address, time_to_live);
577 : }
578 :
579 492 : privoxy_mutex_unlock(&client_tags_mutex);
580 :
581 492 : return JB_ERR_OK;
582 :
583 : }
584 :
585 : /*********************************************************************
586 : *
587 : * Function : disable_client_specific_tag
588 : *
589 : * Description : Disables a client-specific-tag for the client
590 : *
591 : * Parameters :
592 : * 1 : csp = Current client state (buffers, headers, etc...)
593 : * 2 : tag_name = The name of the tag to disable
594 : *
595 : * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
596 : *
597 : *********************************************************************/
598 294 : jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
599 : {
600 : struct client_tag_spec *tag;
601 :
602 294 : privoxy_mutex_lock(&client_tags_mutex);
603 :
604 294 : tag = get_client_specific_tag(csp->config->client_tags, tag_name);
605 294 : if (tag == NULL)
606 : {
607 43 : privoxy_mutex_unlock(&client_tags_mutex);
608 43 : return JB_ERR_PARSE;
609 : }
610 :
611 251 : if (client_has_requested_tag(csp->client_address, tag_name))
612 : {
613 132 : remove_tag_for_client(csp->client_address, tag_name);
614 132 : log_error(LOG_LEVEL_TAGGING,
615 : "Tag '%s' disabled for client '%s'.", tag->name, csp->client_address);
616 : }
617 : else
618 : {
619 119 : log_error(LOG_LEVEL_TAGGING,
620 : "Tag '%s' currently not set for client '%s'.",
621 : tag->name, csp->client_address);
622 : }
623 :
624 251 : privoxy_mutex_unlock(&client_tags_mutex);
625 251 : return JB_ERR_OK;
626 :
627 : }
628 :
629 :
630 : /*********************************************************************
631 : *
632 : * Function : client_tag_match
633 : *
634 : * Description : Compare a client tag against a client tag pattern.
635 : *
636 : * Parameters :
637 : * 1 : pattern = a TAG pattern
638 : * 2 : tag = Client tag to match
639 : *
640 : * Returns : Nonzero if the tag matches the pattern, else 0.
641 : *
642 : *********************************************************************/
643 700980 : int client_tag_match(const struct pattern_spec *pattern,
644 : const struct list *tags)
645 : {
646 : struct list_entry *tag;
647 :
648 700980 : if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
649 : {
650 : /*
651 : * It's not a client pattern and thus shouldn't
652 : * be matched against client tags.
653 : */
654 567460 : return 0;
655 : }
656 :
657 133520 : assert(tags);
658 :
659 135896 : for (tag = tags->first; tag != NULL; tag = tag->next)
660 : {
661 3168 : if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
662 : {
663 792 : log_error(LOG_LEVEL_TAGGING, "Client tag '%s' matches.", tag->str);
664 792 : return 1;
665 : }
666 : }
667 :
668 132728 : return 0;
669 :
670 : }
671 :
672 :
673 : /*********************************************************************
674 : *
675 : * Function : set_client_address
676 : *
677 : * Description : Sets the client address that will be used to enable,
678 : * disable, or apply client tags.
679 : *
680 : * Parameters :
681 : * 1 : csp = Current client state (buffers, headers, etc...)
682 : * 2 : headers = Client headers
683 : *
684 : * Returns : void.
685 : *
686 : *********************************************************************/
687 33010 : void set_client_address(struct client_state *csp, const struct list *headers)
688 : {
689 33010 : if (csp->config->trust_x_forwarded_for)
690 : {
691 : const char *client_address;
692 :
693 33010 : client_address = get_header_value(headers, "X-Forwarded-For:");
694 33010 : if (client_address != NULL)
695 : {
696 2053 : csp->client_address = strdup_or_die(client_address);
697 2053 : log_error(LOG_LEVEL_HEADER,
698 : "Got client address %s from X-Forwarded-For header",
699 : csp->client_address);
700 : }
701 : }
702 :
703 33010 : if (csp->client_address == NULL)
704 : {
705 30957 : csp->client_address = strdup_or_die(csp->ip_addr_str);
706 : }
707 33010 : }
708 :
709 : #else
710 : #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
711 : #endif /* def FEATURE_CLIENT_TAGS */
|